我有一个数组,我想从中删除一些元素。我不能使用
Array.prototype.filter()
,因为我想就地修改数组(因为它节省了内存分配,而且对我来说更重要的是,使代码在我的用例中更简单)。我可以使用 filter
的就地替代方案吗,也许类似于如何将 Array.prototype.forEach()
用作 Array.prototype.map()
的就地变体?
编辑:根据要求提供的最小示例:
function someCallback(array) {
// do some stuff
array.filterInPlace(function(elem) {
var result = /* some logic */
return result;
})
// do some more stuff
}
您可以使用以下内容:
array.splice(0, array.length,...array.filter(/*YOUR FUNCTION HERE*/))
说明:
是否有过滤器的就地替代方案
不,但是自己编写并不难。这是一种排除所有不符合条件的值的方法。
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
被设计为与传递给 Array#filter
的回调具有相同的签名,即 (value, index, array)
。为了与 Array#filter
完全兼容,您还可以接受第四个 thisArg
参数。
forEach
使用
forEach
的一个小优点是它会跳过空槽。这个版本:
thisArg
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');
你可以使用什么
Array#filter
返回一个具有相同元素的数组,但不一定是全部。Array#map
为每个循环返回一些内容,结果是一个与源数组长度相同的数组。Array#forEach
不返回任何内容,但每个元素都会被处理,如上所示。Array#reduce
返回您想要的任何内容。Array#some
/Array#every
返回布尔值。但是上面的任何内容都不会在原位改变长度问题的原始数组。
我建议使用 while 循环,从最后一个元素开始并将 splice 应用于要删除的元素。
这可以保持索引有效,并允许在每个循环中递减。
示例:
var array = [0, 1, 2, 3, 4, 5],
i = array.length;
while (i--) {
if (array[i] % 2) {
array.splice(i, 1);
}
}
console.log(array);
如果您能够添加第三方库,请查看lodash.remove:
predicate = function(element) {
return element == "to remove"
}
lodash.remove(array, predicate)
当前选择的答案效果很好。然而,我希望这个函数成为数组原型的一部分。
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;
}
这样我就可以像这样调用函数了:
const arr = [1, 2, 3, 4];
arr.filterInPlace(x => x > 2);
// [1, 2]
我只是将其保存在名为 Array.js 的文件中,并在需要时使用它。
user663031 答案的稍微简化的 TypeScript 变体:
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);
}
使用
splice()
而不是设置 length
会导致 Chrome 76 上 1400000
迭代速度提高 1.2 倍。
这是一种干净的方法,并且会向后迭代以提高性能。
function filterInPlace<T> (array: T[], predicate: (item: T, index: number, arr: T[]) => unknown) {
for (let i = array.length; i--; i >= 0) {
if (!predicate(array[i]!, i, array)) {
array.splice(i, 1)
}
}
return array
}