Dividing an array by filter function

后端 未结 12 947
抹茶落季
抹茶落季 2020-12-01 11:24

I have a Javascript array that I would like to split into two based on whether a function called on each element returns true or false. Essentially

相关标签:
12条回答
  • 2020-12-01 12:14

    This sounds very similar to Ruby's Enumerable#partition method.

    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 Array to perform this function. In this example, the filter function is executed in the context of the original array (i.e., this will be the original array), and it receives the element and the index of the element as arguments (similar to jQuery's each method):

    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 ] ]
    
    0 讨论(0)
  • 2020-12-01 12:20

    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

    0 讨论(0)
  • 2020-12-01 12:21

    A lot of answers here use Array.prototype.reduce to build a mutable accumulator, and rightfully point out that for large arrays, this is more efficient than, say, using a spread operator to copy a new array each iteration. The downside is that it's not as pretty as a "pure" expression using the short lambda syntax.

    But a way around that is to use the comma operator. In C-like languages, comma is an operator that always returns the right hand operand. You can use this to create an expression that calls a void function and returns a value.

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

    If you take advantage of the fact that a boolean expression implicitly casts to a number as 0 and 1, and you can make it even more concise, although I don't think it's as readable:

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

    Usage:

    const [trues, falses] = partition(['aardvark', 'cat', 'apple'], i => i.startsWith('a'));
    console.log(trues); // ['aardvark', 'apple']
    console.log(falses); // ['cat']
    
    0 讨论(0)
  • 2020-12-01 12:23

    You can use 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

    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' }  ]
    
    0 讨论(0)
  • 2020-12-01 12:24

    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 === false need to be real comparasion ;)

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

    0 讨论(0)
  • 2020-12-01 12:26

    With ES6 you can make use of the spread syntax with 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]]), [[], []]);
    
    0 讨论(0)
提交回复
热议问题