JavaScript - 使用变异过滤数组

8

我希望通过保留原始数组而不创建新数组来筛选数组。

使用 Array.filter() 方法:

getFiltersConfig() {
  return this.config.filter((topLevelConfig) => topLevelConfig.name !== 'origin')
}

什么是在不返回新数组的情况下按值筛选以获得相同结果的最佳方法?

4
为什么你想这么做,有什么应用场景? - Etheryte
过滤器按照定义创建一个新数组,该数组符合您的条件。 - ihkawiss
也许最简单的方法是使用“过滤”然后“拼接”。 - Raymond Chen
@Nit @ihkwiss 我想保留相同的数组,就像使用 Array.splice() 一样。 - Mouad Ennaciri
我对此有一个具体的用例。我需要遍历一个值数组,并从另一个数组中删除相应的值。显然,我不能为每个循环创建一个新数组,而且我需要按值而不是索引进行删除。下面是Keith的答案,似乎是一个很好的解决方案。 - Robert M.
我对此有一个具体的用例。我需要遍历一个值数组,并从另一个数组中删除相应的值。显然,我不能为每次循环创建一个新数组,并且我需要按值而不是索引进行删除。下面是Keith的答案,似乎是一个很好的解决方案。 - undefined
4个回答

7
为了完整起见,我认为展示一个变异数组的变体可能是有意义的。
以下是一个具有简单函数mutationFilter的片段,它将直接过滤数组。请注意,在此函数中,循环反向进行,这是一种删除具有变异数组项的技术。
还有一些测试,以显示Array.filter如何创建新数组,而mutationFilter则没有。
尽管在大多数情况下,使用Array.filter创建新数组通常是您想要的。使用变异数组的一个优点是可以通过引用传递数组,否则您需要将数组包装在另一个对象中。当然,另一个优点是内存,如果您的数组非常庞大,则内联过滤将占用更少的内存。

let arr = ['a','b','a'];
let ref = arr; //keep reference of original arr

function mutationFilter(arr, cb) {
  for (let l = arr.length - 1; l >= 0; l -= 1) {
    if (!cb(arr[l])) arr.splice(l, 1);
  }
}

const cond = x => x !== 'a';

const filtered = arr.filter(cond);
mutationFilter(arr, cond);

console.log(`ref === array -> ${ref === arr}`);
console.log(arr);

console.log(`ref === filtered -> ${ref === filtered}`);
console.log(filtered);


这是一个很酷的解决方案,但是我对示例代码感到困惑。为什么 ref === arr?ref 仍然包含三个元素,而 arr 只有一个。 - Robert M.
这是一个很棒的解决方案,但是我对示例代码感到困惑。为什么 ref === arr?ref 仍然包含三个元素,而 arr 只有一个。 - undefined
@RobertM. 很晚回复,refarr都只会包含1个值,这就是副作用突变的结果,也是常常导致混淆的原因,通常建议避免使用。 - Keith
哦,我明白了,ref包含的元素数量与arr相同,因为它是对原始数组的引用。我之前没有注意到这一点。 - Robert M.

1
我想通过保留相同的数组而不创建新数组来过滤一个数组。
如何在不返回新数组的情况下按值过滤以获得相同的结果?
我有第二个标准的答案,但违反了第一个标准。我怀疑您之所以要“不创建新数组”,可能是因为您只想保留对数组的引用,而不是因为您不想创建新数组(例如出于内存考虑)。
您可以创建一个临时数组来实现您想要的效果。
var temp = this.config.filter((topLevelConfig) => topLevelConfig.name !== 'origin')

然后将原始数组的长度设为0,并使用push.apply()“就地”推入值
this.config.length = 0; //clears the array
this.config.push.apply(this.config, temp); //adds what you want to the array of the same reference

1
这是一个聪明的解决方案,适用于特定的使用情景。我希望原帖作者能更明确地说明他为什么需要这个。 - Robert M.

-1
你可以像这样定义自己的方法:

if(!Array.prototype.filterThis){
  Array.prototype.filterThis = function (callBack){
          if(typeof callBack !== 'function')
              throw new TypeError('Argument must of type <function>');
    let t = [...this];
    this.length = 0;
    for(let e of t) if(callBack(e)) this.push(e);
          return this;
  }
}

let a = [1,2,3,4,5,5,1,5];
a.filterThis(x=>x!=5);
console.log(a);

警告:在更改内置原型时要非常谨慎。我甚至会说,除非你正在制作一个 polyfill,否则不要碰它。它可能引起的错误非常微妙,非常难以调试。

在修改内置原型时要非常小心。我甚至会说,除非你正在制作一个 polyfill,否则不要碰它。它可能会引起非常微妙且难以调试的错误。 - Keith
@Keith,我没有覆盖任何内置方法。我创建了一个新方法。 - A Rogue Otaku
不,你修改了内置Array的原型.. 有关详情请查看 -> https://dev59.com/T2Yr5IYBdhLWcg3wAFg0 - Keith
@Keith。嗯,我明白你的意思。基本上,由于这不是标准方法,以后可能会通过ES规范添加一个带有此名称的方法,那么就会出现问题,对吧? - A Rogue Otaku
是的,你永远不知道 ES 规范有一天可能会添加一个 filterThis 方法 :) 这种情况以前也发生过,显然 MooTools 在原型中添加了一个 contains 方法,并且实际上与 ES6 的 contains 冲突,引起了无尽的麻烦。 - Keith
嗯..我只是要在我的代码中添加一个检查,看看filterThis是否已经存在,并在下面添加一个警告文本,以及你的评论。虽然我不会删除答案,但这将在下次尝试更改内置原型时提醒我。 :D - A Rogue Otaku

-6

不确定为什么您想要进行变异,但如果您真的想这样做,也许可以将其分配回自身?

let arr = ['a','b','a'];

arr = arr.filter(x => x !== 'a');

console.log(arr)


15
以后参考,记住这并不是改变数组,而是创建一个新的数组。 - Keith
是的,这不是他们要求的。当然它在同一个变量中,但它是一个新数组。 - Intervalia
@Keith,如果我在使用相同的变量时创建一个新的变量会有什么问题? - Ooker
@Ooker 没有什么!但它并没有像OP要求的那样改变数组。也许OP误解了“改变”的含义,所以这个回答被接受了。但根据标题来看,完全是错误的,可能解释了为什么会有负评。 - Keith

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接