What is the in-place alternative to Array.prototype.filter()
Solution 1:
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');
Solution 2:
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
Solution 3:
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);
Solution 4:
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)