Javascript 用过滤器函数划分数组

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11731072/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 06:53:15  来源:igfitidea点击:

Dividing an array by filter function

javascript

提问by Mike Chen

I have a Javascript array that I would like to split into two based on whether a function called on each element returns trueor false. Essentially, this is an array.filter, but I'd like to also have on hand the elements that were filtered out.

我有一个 Javascript 数组,我想根据每个元素上调用的函数是返回true还是false. 本质上,这是一个array.filter,但我还想手头有被过滤的元素。

Currently, my plan is to use array.forEachand call the predicate function on each element. Depending on whether this is true or false, I will push the current element onto one of the two new arrays. Is there a more elegant or otherwise better way to do this? An array.filterwhere the will push the element onto another array before it returns false, for instance?

目前,我的计划是array.forEach在每个元素上使用和调用谓词函数。根据这是对还是错,我会将当前元素推送到两个新数组之一。有没有更优雅或更好的方法来做到这一点?例如array.filter,在它返回之前将元素推送到另一个数组的地方false

回答by braza

With ES6 you can make use of the spread syntax with reduce:

在 ES6 中,您可以通过 reduce 使用扩展语法:

function partition(array, isValid) {
  return array.reduce(([pass, fail], elem) => {
    return isValid(elem) ? [[...pass, elem], fail] : [pass, [...fail, elem]];
  }, [[], []]);
}

const [pass, fail] = partition(myArray, (e) => e > 5);

Or on a single line:

或者在一行上:

const [pass, fail] = a.reduce(([p, f], e) => (e > 5 ? [[...p, e], f] : [p, [...f, e]]), [[], []]);

回答by Zoltan Kochan

You can use lodash.partition

您可以使用lodash.partition

var users = [
  { 'user': 'barney',  'age': 36, 'active': false },
  { 'user': 'fred',    'age': 40, 'active': true },
  { 'user': 'pebbles', 'age': 1,  'active': false }
];

_.partition(users, function(o) { return o.active; });
// → objects for [['fred'], ['barney', 'pebbles']]

// The `_.matches` iteratee shorthand.
_.partition(users, { 'age': 1, 'active': false });
// → objects for [['pebbles'], ['barney', 'fred']]

// The `_.matchesProperty` iteratee shorthand.
_.partition(users, ['active', false]);
// → objects for [['barney', 'pebbles'], ['fred']]

// The `_.property` iteratee shorthand.
_.partition(users, 'active');
// → objects for [['fred'], ['barney', 'pebbles']]

or ramda.partition

ramda.partition

R.partition(R.contains('s'), ['sss', 'ttt', 'foo', 'bars']);
// => [ [ 'sss', 'bars' ],  [ 'ttt', 'foo' ] ]

R.partition(R.contains('s'), { a: 'sss', b: 'ttt', foo: 'bars' });
// => [ { a: 'sss', foo: 'bars' }, { b: 'ttt' }  ]

回答by Yaremenko Andrii

You can use reduce for it:

您可以使用 reduce :

function partition(array, callback){
  return array.reduce(function(result, element, i) {
    callback(element, i, array) 
      ? result[0].push(element) 
      : result[1].push(element);

        return result;
      }, [[],[]]
    );
 };

Update. Using ES6 syntax you also can do that using recursion:

更新。使用 ES6 语法,您也可以使用递归来做到这一点:

function partition([current, ...tail], f, [left, right] = [[], []]) {
    if(current === undefined) {
        return [left, right];
    }
    if(f(current)) {
        return partition(tail, f, [[...left, current], right]);
    }
    return partition(tail, f, [left, [...right, current]]);
}

回答by Brandan

This sounds very similar to Ruby's Enumerable#partitionmethod.

这听起来与Ruby 的Enumerable#partition方法非常相似。

If the function can't have side-effects (i.e., it can't alter the original array), then there's no more efficient way to partition the array than iterating over each element and pushing the element to one of your two arrays.

如果函数不能产生副作用(即它不能改变原始数组),那么没有比迭代每个元素并将元素推送到两个数组之一更有效的方法来分区数组。

That being said, it's arguably more "elegant" to create a method on Arrayto perform this function. In this example, the filter function is executed in the context of the original array (i.e., thiswill be the original array), and it receives the element and the index of the element as arguments (similar to jQuery's eachmethod):

话虽如此,创建一个方法Array来执行这个功能可以说是更“优雅” 。在这个例子中,过滤器函数在原始数组的上下文中执行(即,this将是原始数组),它接收元素和元素的索引作为参数(类似于jQuery 的each方法):

Array.prototype.partition = function (f){
  var matched = [],
      unmatched = [],
      i = 0,
      j = this.length;

  for (; i < j; i++){
    (f.call(this, this[i], i) ? matched : unmatched).push(this[i]);
  }

  return [matched, unmatched];
};

console.log([1, 2, 3, 4, 5].partition(function (n, i){
  return n % 2 == 0;
}));

//=> [ [ 2, 4 ], [ 1, 3, 5 ] ]

回答by UDrake

I came up with this little guy. It uses for each and all that like you described, but it looks clean and succinct in my opinion.

我想到了这个小家伙。它用于您所描述的所有内容,但在我看来它看起来干净简洁。

//Partition function
function partition(array, filter) {
  let pass = [], fail = [];
  array.forEach((e, idx, arr) => (filter(e, idx, arr) ? pass : fail).push(e));
  return [pass, fail];
}

//Run it with some dummy data and filter
const [lessThan5, greaterThanEqual5] = partition([0,1,4,3,5,7,9,2,4,6,8,9,0,1,2,4,6], e => e < 5);

//Output
console.log(lessThan5);
console.log(greaterThanEqual5);

回答by codename-

In filter function you can push your false items into another variable outside function:

在过滤器函数中,您可以将错误项推送到函数外的另一个变量中:

var bad = [], good = [1,2,3,4,5];
good = good.filter(function (value) { if (value === false) { bad.push(value) } else { return true});

Of course value === falseneed to be real comparasion ;)

当然value === false需要真正的比较;)

But it do almost that same operation like forEach. I think you should use forEachfor better code readability.

但它执行的操作几乎与forEach. 我认为你应该使用forEach更好的代码可读性。

回答by qwertymk

Try this:

尝试这个:

function filter(a, fun) {
    var ret = { good: [], bad: [] };
    for (var i = 0; i < a.length; i++)
        if (fun(a[i])
            ret.good.push(a[i]);
        else
            ret.bad.push(a[i]);
    return ret;
}

DEMO

演示

回答by Vereb

What about this?

那这个呢?

[1,4,3,5,3,2].reduce( (s, x) => { s[ x > 3 ].push(x); return s;} , {true: [], false:[]} )

Probably this is more efficient then the spread operator

可能这比传播运算符更有效

Or a bit shorter, but uglier

或者更短,但更丑

[1,4,3,5,3,2].reduce( (s, x) => s[ x > 3 ].push(x)?s:s , {true: [], false:[]} )

回答by Matsumoto Kazuya

Easy to read one.

轻松读一读。

const partition = (arr, condition) => {
    const trues = arr.filter(el => condition(el));
    const falses = arr.filter(el => !condition(el));
    return [trues, falses];
};

// sample usage
const nums = [1,2,3,4,5,6,7]
const [evens, odds] = partition(nums, (el) => el%2 == 0)

回答by Andreas Gassmann

I ended up doing this because it's easy to understand (and fully typed with typescript).

我最终这样做是因为它很容易理解(并且完全用打字稿打字)。

const partition = <T>(array: T[], isValid: (element: T) => boolean): [T[], T[]] => {
  const pass: T[] = []
  const fail: T[] = []
  array.forEach(element => {
    if (isValid(element)) {
      pass.push(element)
    } else {
      fail.push(element)
    }
  })
  return [pass, fail]
}

// usage
const [pass, fail] = partition([1, 2, 3, 4, 5], (element: number) => element > 3)