Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

People also ask

What to use instead of array includes?

indexOf() The Array#indexOf() function is a common alternative to includes() . The indexOf() function returns the first index in the array at which it found valueToFind , or -1 otherwise.

What is array prototype filter useful for?

Array.prototype.filter() The filter() method creates a shallow copy of a portion of a given array, filtered down to just the elements from the given array that pass the test implemented by the provided function.

Can we use any alternatives of array?

T-SQL doesn't support arrays (groups of objects that have the same attributes and that you can use techniques such as subscripting to address individually). But you can use local or global temporary tables, which the main article describes, as an alternative to an array.

What is array filter ()?

JavaScript Array filter() The filter() method creates a new array filled with elements that pass a test provided by a function. The filter() method does not execute the function for empty elements. The filter() method does not change the original array.


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

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

What you could use

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

But nothing from above is mutating 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);

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)

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.


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.