通过过滤函数划分数组

Mik*_*hen 64 javascript

我有一个Javascript数组,我想根据每个元素上调用的函数是返回true还是分成两个false.从本质上讲,这是一个array.filter,但我也想了解过滤的元素.

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

bra*_*aza 46

使用ES6,您可以使用spread语法和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);
Run Code Online (Sandbox Code Playgroud)

或者在一行上:

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

  • 这将为原始中的每个元素创建两个新数组.虽然一个数组只有两个元素,但另一个数组随数组的大小增长.因此,这将非常缓慢并浪费大量内存.(您可以通过推送执行相同操作,效率更高.) (7认同)
  • 对我来说,lodash分区或只是forEach会更容易理解,但即便如此,努力 (4认同)
  • 这是 O(n) 问题的 O(n^2) 解决方案。由于扩展运算符(或复制(部分)数组的任何其他方法)必须为数组的每个成员执行工作,并且此解决方案对原始数组中的每个元素多次应用此类操作。 (4认同)

Zol*_*han 25

你可以使用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']]
Run Code Online (Sandbox Code Playgroud)

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' }  ]
Run Code Online (Sandbox Code Playgroud)


小智 21

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

//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);
Run Code Online (Sandbox Code Playgroud)

  • 恕我直言,这个解决方案比一些获得更多赞成票的解决方案要好得多。易于阅读,单次遍历数组,并且不分配和重新分配分区结果数组。我还喜欢它向过滤器函数公开所有三个常见的过滤器值(值、索引和整个数组)。这将使该函数更加可重用。 (7认同)

Bra*_*dan 14

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

如果函数不能有副作用(即,它不能改变原始数组),那么对于对数组进行分区而不是迭代每个元素并将元素推送到两个数组中的一个时,没有更有效的方法.

话虽这么说,创建一个方法Array来执行这个功能可能更"优雅" .在这个例子中,filter函数在原始数组的上下文中执行(即,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 ] ]
Run Code Online (Sandbox Code Playgroud)

  • 对于现代读者,请不要向全局标准库对象添加方法.这很危险,很可能被覆盖,导致神秘和破碎的行为.一个普通的旧函数,适当的作用域,更安全,并且调用myFunc(数组)并不比array.myFunc()更"优雅". (8认同)

par*_*omi 12

这里的很多答案都用于Array.prototype.reduce构建可变累加器,并正确地指出,对于大型数组,这比使用扩展运算符在每次迭代时复制新数组更有效。缺点是它不如使用短 lambda 语法的“纯”表达式漂亮。

但解决这个问题的方法是使用逗号运算符。在类 C 语言中,逗号是一个始终返回右侧操作数的运算符。您可以使用它来创建一个调用 void 函数并返回值的表达式。

function partition(array, predicate) {
    return array.reduce((acc, item) => predicate(item)
        ? (acc[0].push(item), acc)
        : (acc[1].push(item), acc), [[], []]);
}
Run Code Online (Sandbox Code Playgroud)

如果您利用布尔表达式隐式转换为数字 0 和 1 的事实,您可以使其更加简洁,尽管我认为它不那么可读:

function partition(array, predicate) {
    return array.reduce((acc, item) => (acc[+!predicate(item)].push(item), acc), [[], []]);
}
Run Code Online (Sandbox Code Playgroud)

用法:

const [trues, falses] = partition(['aardvark', 'cat', 'apple'], i => i.startsWith('a'));
console.log(trues); // ['aardvark', 'apple']
console.log(falses); // ['cat']
Run Code Online (Sandbox Code Playgroud)


Yar*_*rii 9

您可以使用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;
      }, [[],[]]
    );
 };
Run Code Online (Sandbox Code Playgroud)

  • 恕我直言,第一个解决方案(推送)是随着数组大小的增长而获得更好的性能。 (3认同)
  • @buncis。第一种方法检查每个元素一次,只需将该元素推送到适当的数组即可。第二种方法为每个元素构造“[...left, current]”或“[...right, current]”。我不知道确切的内部结构,但我确信构造比简单地将元素推入数组更昂贵。此外,作为一般规则,*递归*比*迭代*更昂贵,因为它每次都涉及创建一个“堆栈帧”。 (3认同)

cod*_*me- 7

在过滤函数中,您可以将虚假项目推送到函数外的另一个变量:

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

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

但它几乎完成了同样的操作forEach.我认为你应该使用forEach更好的代码可读性.

  • 同意上面的海报...将这种逻辑放入过滤器函数中感觉有点臃肿且难以管理。 (2认同)

Ver*_*reb 7

那这个呢?

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

可能这比扩展运算符更有效

或者更短,但更丑

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

Run Code Online (Sandbox Code Playgroud)


qwe*_*ymk 6

尝试这个:

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;
}
Run Code Online (Sandbox Code Playgroud)

演示版


Mat*_*uya 5

轻松读一读。

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)
Run Code Online (Sandbox Code Playgroud)

  • 缺点是你做了 2 个循环而不是一个 (8认同)

And*_*ann 5

我最终这样做是因为它很容易理解:

const partition = (array, isValid) => {
  const pass = []
  const fail = []
  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) => element > 3)
Run Code Online (Sandbox Code Playgroud)

相同的方法包括打字稿的类型:

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)
Run Code Online (Sandbox Code Playgroud)