JavaScript ES6,如果存在则删除数组项的最佳方法,如果不存在则添加。

13

我在这里编写了一些代码来添加一个元素到数组中,具体逻辑如下:

如果该元素已经存在于数组中,则函数应该从数组中删除该元素并返回不包含该元素的数组,否则它应该将该元素附加到数组中并返回整个数组。

考虑到这一点,我在想是否有更好的方法来实现这个功能。以下是我编写的代码。

function filterArray(arr, obj, key) {
  for (let i = 0; i < arr.length; i++) {
    if (arr[i][key] === obj[key]) {
      const newArr = [...arr]
      newArr.splice(i, 1)
      return newArr
    }
  }
  return [...arr, obj]
}

const fruits = [
  { id: 1, fruit: "Apple" },
  { id: 2, fruit: "Banana" },
  { id: 3, fruit: "Pineapple" }
]

const removedBanana = filterArray(fruits, {
  id: 3,
  fruit: "Banana"
}, "fruit")

const addedStrawberry = filterArray(fruits, {
  id: 4,
  fruit: "Strawberry"
}, "fruit")

console.log(removedBanana) // [ { id: 1, fruit: 'Apple' }, { id: 3, fruit: 'Pineapple' } ]
console.log(addedStrawberry)
// [
//   { id: 1, fruit: 'Apple' },
//   { id: 2, fruit: 'Banana' },
//   { id: 3, fruit: 'Pineapple' },
//   { id: 4, fruit: 'Strawberry' }
// ]

在ES6中有更好的方法可以做到这一点吗?

编辑
如果可能的话,我想只迭代一次数组,达到O(n)的算法。


你想要改变数组吗? - Nina Scholz
@NinaScholz 是的 - Ricardo Mendes
你的代码展示了一个新数组... - Nina Scholz
抱歉,@NinaScholz,我不想突变,我想创建一个新的。 - Ricardo Mendes
4个回答

8

这种方法通过寻找索引并在找到时进行splice操作,或者将对象推送到数组中来改变数组。结果与传递的数组具有相同的对象引用。

function filterArray(array, object, key) {
    var index = array.findIndex(o => o[key] === object[key]);
    if (index === -1) array.push(object);
    else array.splice(index, 1);
    return array;
}

const
    fruits = [{ id: 1, fruit: "Apple" }, { id: 2, fruit: "Banana" }, { id: 3, fruit: "Pineapple" }];

console.log(filterArray(fruits, { id: 3, fruit: "Banana" }, "fruit"));
console.log(filterArray(fruits, { id: 4, fruit: "Strawberry" }, "fruit"));
.as-console-wrapper { max-height: 100% !important; top: 0; }

如果采用不可变方法,可以在函数头部获取一份副本,并执行与上述相同的操作。

function filterArray([...array], object, key) {

function filterArray(array, object, key) {
    var index = array.findIndex(o => o[key] === object[key]);
    if (index === -1) array.push(object);
    else array.splice(index, 1);
    return array;
}

const
    fruits = [{ id: 1, fruit: "Apple" }, { id: 2, fruit: "Banana" }, { id: 3, fruit: "Pineapple" }];

console.log(filterArray(fruits, { id: 3, fruit: "Banana" }, "fruit"));
console.log(filterArray(fruits, { id: 4, fruit: "Strawberry" }, "fruit"));
.as-console-wrapper { max-height: 100% !important; top: 0; }


得移动到比杜塞尔多夫快12小时的时区才能有机会获得一些声望分数。想用三元操作符做这个,但在这种情况下 splice()/push() 组合似乎是最好的选择(特别是和 findIndex() 一起使用)。已点赞。 - Yevhen Horbunkov
@YevgenGorbunkov,三元运算符无法工作,因为参数不同。 - Nina Scholz
三元运算符在“变异”方法中完美地发挥作用。 - Yevhen Horbunkov
...同样适用于函数参数中的展开语法,以及非变异。 - Yevhen Horbunkov

3
你可以使用Array.some()来检查对象是否存在,如果存在,则过滤掉它。如果不存在,则添加它。

function filterArray(arr, obj, key) {
  return arr.some(o => obj[key] === o[key]) ? // if you can find the object
    arr.filter(o => obj[key] !== o[key]) // remove it
    :  
    [...arr, obj] // or add it if not
}

const fruits = [{"id":1,"fruit":"Apple"},{"id":2,"fruit":"Banana"},{"id":3,"fruit":"Pineapple"}]

const removedBanana = filterArray(fruits, { id: 3, fruit: "Banana" }, "fruit")
const addedStrawberry = filterArray(fruits, { id: 4, fruit: "Strawberry" }, "fruit")

console.log(removedBanana) // [ { id: 1, fruit: 'Apple' }, { id: 3, fruit: 'Pineapple' } ]
console.log(addedStrawberry)
// [
//   { id: 1, fruit: 'Apple' },
//   { id: 2, fruit: 'Banana' },
//   { id: 3, fruit: 'Pineapple' },
//   { id: 4, fruit: 'Strawberry' }
// ]


filter() 循环遍历整个数组,而 some() 则在匹配到结果之前一直循环,最坏情况下需要两次循环。虽然时间复杂度仍为 O(n),但似乎不是最佳性能解决方案。我猜想,可能有更简洁的方法。参考链接 - Yevhen Horbunkov
请注意解决方案之间的区别 - 我的解决方案不会改变数组。如果要保持不可变性而不使用过滤器,则仍需要首先找到项目的索引 - O(n),如果存在,则也要复制数组(O(n))。因此,不可变将需要O(2n)-> O(n)。 - Ori Drori

1
这个问题有一个简单的解决方案:

function addObjToArray (arr, obj, key) {
  const resultArr = arr.filter(arrObj => arrObj[key] !== obj[key])
  if (resultArr.length === arr.length) resultArr.push(obj)
  return resultArr
}
filter 方法将返回一个仅包含与 obj[key] 不同的对象的数组。如果过滤后的数组与原始数组相等,则将对象推送到 resultArr 中,否则只会返回初始的 resultArr

这是我的同事(https://stackoverflow.com/users/12691758/guilherme-ca%c3%a7ador-monteiro)提出的解决方案。

你觉得怎么样?


从 https://stackoverflow.com/users/12691758/guilherme-ca%c3%a7ador-monteiro - Felipe Marcon

0
根据您的要求,以及在评论中提出的额外要求 - 集合应该变异,使用ES6 Map将会是更好的工具:

const fruits = new Map([
  { id: 1, fruit: "Apple" },
  { id: 2, fruit: "Banana" },
  { id: 3, fruit: "Pineapple" }
].map (o => [o.fruit, o]));
fruits.key = "fruit"; // register the key field once
fruits.toggleItem = function (obj) {
    if (!this.delete(obj[this.key])) this.set(obj[this.key], obj);
}

fruits.toggleItem({id: 3, fruit: "Banana"});

console.log([...fruits.values()]);

fruits.toggleItem({id: 4, fruit: "Strawberry"});

console.log([...fruits.values()]);


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