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
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 arrayforEach returns nothing, but every element is processes, like above.reduce returns what ever you want.some/every returns a boolean valueBut 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);
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.
You could use the following:
array.splice(0, array.length,...array.filter(/*YOUR FUNCTION HERE*/))
Explanation:
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)
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.
forEachUsing forEach has the minor advantage that it will skip empty slots. This version:
thisArgfunction 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');
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.