What is the in-place alternative to Array.prototype.filter()

后端 未结 6 1131
面向向阳花
面向向阳花 2020-11-29 07:37

I\'ve got an array that I would like to remove some elements from. I can\'t use Array.prototype.filter(), because I want to modify the array in place (because i

相关标签:
6条回答
  • 2020-11-29 07:49

    What you could use

    • filter returns an array with the same elements, but not necesserily all.
    • map returns something for each loop, the result is an array with the same length as the source array
    • forEach returns nothing, but every element is processes, like above.
    • reduce returns what ever you want.
    • some/every returns a boolean value

    But nothing from above is mutilating the original array in question of length in situ.

    I suggest to use a while loop, beginning from the last element and apply splice to the element, you want to remove.

    This keeps the index valid and allows to decrement for every loop.

    Example:

    var array = [0, 1, 2, 3, 4, 5],
        i = array.length;
    
    while (i--) {
        if (array[i] % 2) {
            array.splice(i, 1);
        }
    }
    console.log(array);

    0 讨论(0)
  • 2020-11-29 07:49

    The currently selected answer works perfectly fine. However, I wanted this function to be a part of the Array prototype.

    Array.prototype.filterInPlace = function(condition, thisArg) {
        let j = 0;
    
        this.forEach((el, index) => {
            if (condition.call(thisArg, el, index, this)) {
                if (index !== j) {
                    this[j] = el;
                }
                j++;
            }
        })
    
        this.length = j;
        return this;
    }
    

    With this I can just call the function like so:

    const arr = [1, 2, 3, 4];
    arr.filterInPlace(x => x > 2);
    // [1, 2]
    

    I just keep this in a file called Array.js and require it when needed.

    0 讨论(0)
  • 2020-11-29 07:51

    You could use the following:

    array.splice(0, array.length,...array.filter(/*YOUR FUNCTION HERE*/))
    

    Explanation:

    • Splice acts in place
    • First argument means we start at the start of the array
    • Second means we delete the entire array
    • Third means we replace it with its filtered copy
    • The ... is the spread operator (ES6 only) and changes each member of the array into a separate argument
    0 讨论(0)
  • If you are able to add a third-party library, have a look at lodash.remove:

    predicate = function(element) {
      return element == "to remove"
    }
    lodash.remove(array, predicate)
    
    0 讨论(0)
  • 2020-11-29 08:08

    Is there an in-place alternative to filter

    No, but it's not hard to write your own. Here is an approach which squeezes out all the values which fail a condition.

    function filterInPlace(a, condition) {
      let i = 0, j = 0;
    
      while (i < a.length) {
        const val = a[i];
        if (condition(val, i, a)) a[j++] = val;
        i++;
      }
    
      a.length = j;
      return a;
    }
    

    condition is designed to have the same signature as the callback passed to Array#filter, namely (value, index, array). For complete compatibility with Array#filter, you could also accept a fourth thisArg parameter.

    Using forEach

    Using forEach has the minor advantage that it will skip empty slots. This version:

    • Compacts arrays with empty slots
    • Implements thisArg
    • Skipps the assignment, if we have not yet encountered a failing element

    function filterInPlace(a, condition, thisArg) {
      let j = 0;
    
      a.forEach((e, i) => { 
        if (condition.call(thisArg, e, i, a)) {
          if (i!==j) a[j] = e; 
          j++;
        }
      });
    
      a.length = j;
      return a;
    }
    
    a = [ 1,, 3 ];
    document.write('<br>[',a,']');
    
    filterInPlace(a, x=>true);
    document.write('<br>[',a,'] compaction when nothing changed');
    
    b = [ 1,,3,,5 ];
    document.write('<br>[',b,']');
    
    filterInPlace(b, x=>x!==5);
    document.write('<br>[',b,'] with 5 removed');

    0 讨论(0)
  • 2020-11-29 08:08

    A slightly simplified TypeScript variant of user663031's answer:

    function filter_in_place<T>(array: Array<T>, condition: (value: T) => boolean)
    {
        let next_place = 0;
    
        for (let value of array)
        {
            if (condition(value))
                array[next_place++] = value;
        }
    
        array.splice(next_place);
    }
    

    Using splice() instead of setting the length results in a 1.2x speedup for 1400000 iterations on Chrome 76.

    0 讨论(0)
提交回复
热议问题