用过滤器函数划分数组

IT技术 javascript
2021-02-26 08:20:39

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

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

6个回答

在 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);

或者在一行上:

const [pass, fail] = a.reduce(([p, f], e) => (e > 5 ? [[...p, e], f] : [p, [...f, e]]), [[], []]);
这将为原始元素中的每个元素创建两个新数组。虽然一个数组只有两个元素,但另一个数组会随着数组的大小而增长。因此,这将非常缓慢并浪费大量内存。(你可以用 push 做同样的事情,这样效率会更高。)
2021-04-25 08:20:39
对我来说,lodash 分区或只是 forEach 会更容易理解,但即便如此,也很好
2021-04-28 08:20:39
谢谢,节省了我的时间!
2021-05-01 08:20:39
这是 O(n) 问题的 O(n^2) 解决方案。由于扩展运算符(或任何其他复制(部分)数组的方法)必须为数组的每个成员工作,因此该解决方案对原始数组中的每个元素多次应用此类操作。
2021-05-04 08:20:39
这真的很有用,布拉扎。其他人注意:下面有一些更具可读性的版本。
2021-05-12 08:20:39

您可以使用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']]

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' }  ]

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

//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);

恕我直言,这个解决方案比一些拥有更多赞成票的解决方案要好得多。易于阅读,单次遍历数组,不分配和重新分配分区结果数组。我也喜欢它向过滤器函数(值、索引和整个数组)公开所有三个常见的过滤器值。这将使此功能更可重用。
2021-04-24 08:20:39
我也非常喜欢这一款,因为它的简洁和优雅。话虽如此,我发现它比for@qwertymk 的旧答案中的简单循环要慢得多例如,对于包含 100,000 个元素的数组,它在我的系统上的速度是原来的两倍。
2021-05-13 08:20:39

您可以使用 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;
      }, [[],[]]
    );
 };

更新。使用 ES6 语法,您还可以使用递归(已更新以避免在每次迭代时创建新数组):

function partition([current, ...tail], f, left = [], right = []) {
    if(current === undefined) {
        return [left, right];
    }
    if(f(current)) {
        left.push(current);
        return partition(tail, f, left, right);
    }
    right.push(current);
    return partition(tail, f, left, right);
}
@buncis 不仅必须为源数组中的每个元素构造一个数组(在 JS 中构造东西是高度优化的,这还不是最糟糕的),而且每次这样做时都会复制源数组。这是一个 O(n) 操作(工作量与数组大小成正比)。它对源数组中的每个成员重复此操作,使工作总量与数组中的元素数成平方。如果工作可以在与元素数量成正比的多个操作中完成,这显然不是很好。
2021-04-22 08:20:39
@ToolmakerSteve 你能详细说明为什么吗?我还阅读了对最佳答案的评论,但仍然困惑为什么
2021-04-28 08:20:39
恕我直言,随着数组大小的增长,第一个解决方案(推送)具有更好的性能。
2021-05-02 08:20:39
@buncis。第一种方法检查每个元素一次,只需将该元素推送到适当的数组。第二种方法构造[...left, current][...right, current]- 为每个元素。我不知道确切的内部结构,但我确信构造比简单地将元素推送到数组更昂贵。此外,作为一般规则,递归迭代更昂贵,因为它每次都涉及创建一个“堆栈帧”。
2021-05-10 08:20:39

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

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

话虽如此,创建一个方法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 ] ]
对于现代读者,请不要向全局标准库对象添加方法。它很危险,并且可能会被覆盖,从而导致神秘和破碎的行为。一个普通的旧函数,范围适当,更安全,调用 myFunc(array) 并不比 array.myFunc() 更“优雅”。
2021-05-11 08:20:39