从对象数组中删除相同的值

5
我想通过比较两个数组来从数组中删除相同的对象。
示例数据:
arr1 = [
  {id: 1, name: "a"},
  {id: 2, name: "b"},
  {id: 3, name: "c"},
  {id: 4, name: "d"},
];

arr2 = [
  {id: 1, name: "a"},
  {id: 4, name: "d"},
];

let newArray = []; // new array with with no same values it should be unique.
arr1.map((val, i)=>{
   arr2.map((val2)=>{
    if(val.id == val2.id){
       console.log('Matched At: '+ i) // do nothing
    }else{
      newArray.push(val);
    }
   })
})
console.log(newArray); // e.g: [{id: 2, name: "b"}, {id: 3, name: "c"},];

这个回答解决了您的问题吗?从JS数组中删除重复值 - Christos Lytras
1
如果arr2包含一些要合并的唯一元素怎么办?例如 arr2 = [ {id: 1, name: "a"}, {id: 4, name: "d"}, {id: 5, name: "x"}, ] - qiAlex
8个回答

7

Array.filter 结合非 Array.some

这里的技巧也是不使用 some,..

const arr1 = [
  {id: 1, name: "a"},
  {id: 2, name: "b"},
  {id: 3, name: "c"},
  {id: 4, name: "d"},
], arr2 = [
  {id: 1, name: "a"},
  {id: 4, name: "d"},
];

const newArray=arr1.filter(a=>!arr2.some(s=>s.id===a.id));

console.log(newArray);
.as-console-wrapper { max-height: 100% !important; top: 0; }

如评论所述,这个问题可以略微不同地解释。如果你还想从arr2中获取唯一项,那么你只需将它执行两次并进行合并即可。换句话说:检查不在arr2中是否在arr1中,然后再检查不在arr1中但在arr2中的元素。
例如...
const notIn=(a,b)=>a.filter(f=>!b.some(s=>f.id===s.id));
const newArray=[...notIn(arr1, arr2), ...notIn(arr2, arr1)];

更新 2: 时间复杂度,正如 qiAlex 提到的,在循环中还有循环。虽然 some 在找到匹配项时会短路,但如果数据集变大,事情可能会变慢。这就是使用 SetMap 的地方。
因此,为了解决这个问题,使用一个 Set
const notIn=(a,b)=>a.filter(a=>!b.has(a.id));
const newArray=[
  ...notIn(arr1, new Set(arr2.map(m=>m.id))),
  ...notIn(arr2, new Set(arr1.map(m=>m.id)))
];

如果 arr2 包含一些需要合并的独特元素呢? - qiAlex
1
@qiAlex 你的意思是像独特的合并吗?我需要和 OP 确认一下,对我来说,我理解这个问题是返回一个不包含另一个数组的数组。 - Keith
起初我像你一样理解了这个问题,这是一个合理的假设,但OP没有明确说明。也有可能将问题理解为应该将两个数组转换为一个数组,排除重复项,但从理论上讲,arr1arr2都可以包含唯一的项。 - qiAlex
1
@qiAlex,你基本上可以用同样的方法做两次然后合并。我会更新答案并加入这个信息。 - Keith
在我的情况下,arr2 中没有唯一的值,所以答案完美地奏效了。 - Najam Us Saqib

2
const isInArray = (arr, id, name) => arr.reduce((result, curr) => ((curr.name === name && curr.id === id) || result), false)

const newArray = arr1.reduce((result, curr) => (isInArray(arr2, curr.id, curr.name) ? result : result.concat(curr)), [])

1
我们可以通过检查当前数组中是否不包含some元素来过滤值:
const result = arr1.reduce((a, c) => {
  if (!arr2.some(a2 => a2.id === c.id))
      a.push(c);
  return a;
}, [])

一个例子:

let arr1 = [
  {id: 1, name: "a"},
  {id: 2, name: "b"},
  {id: 3, name: "c"},
  {id: 4, name: "d"},
];

let arr2 = [
  {id: 1, name: "a"},
  {id: 4, name: "d"},
];

const result = arr1.reduce((a, c) => {
  if (!arr2.some(a2 => a2.id === c.id))
      a.push(c);
  return a;
}, [])

console.log(result);


1
你可以使用filter()方法来更新你的代码,而不是使用.map()方法,例如:

const arr1 = [
  {id: 1, name: "a"},
  {id: 2, name: "b"},
  {id: 3, name: "c"},
  {id: 4, name: "d"},
], arr2 = [
  {id: 1, name: "a"},
  {id: 4, name: "d"},
];

let newArray = []; // new array with with no same values it should be unique.
newArray = arr1.filter(function(a) {
    for(var i=0; i < arr2.length; i++){
      if(a.id == arr2[i].id) return false;
    }
    return true;
});
console.log(newArray);
.as-console-wrapper { max-height: 100% !important; top: 0; }


1

您可以使用Array.prototype.some检查第一个数组中的每个元素,其id是否存在于第二个数组中。如果该元素不存在,则只需产生它。

const arr1 = [
  {id: 1, name: "a"},
  {id: 2, name: "b"},
  {id: 3, name: "c"},
  {id: 4, name: "d"},
];

const arr2 = [
  {id: 1, name: "a"},
  {id: 4, name: "d"},
];

const result = arr1.filter(x => !arr2.some(y => y.id === x.id));

console.log(result);


1
我认为一个简单的比较器可以用于获取差异,然后将它们连接起来。 使用这种方法,您不需要检查哪个数组更大。

arr1 = [  {id: 1, name: "a"},  {id: 2, name: "b"},  {id: 3, name: "c"},  {id: 4, name: "d"}];

arr2 = [  {id: 1, name: "a"},  {id: 4, name: "d"},];

function localComparer(b){
  return function(a){
    return b.filter(
    function(item){
      return item.id == a.id && item.name == a.name
    }).length == 0;
  }
}

var onlyInArr1 = arr1.filter(localComparer(arr2));
var onlyInArr2 = arr2.filter(localComparer(arr1));

console.log(onlyInArr1.concat(onlyInArr2));


1
尝试这个 -

const arr1 = [
  {id: 1, name: "a"},
  {id: 2, name: "b"},
  {id: 3, name: "c"},
  {id: 4, name: "d"},
];

const arr2 = [
  {id: 1, name: "a"},
  {id: 4, name: "d"},
];

const arr3 = [...arr1, ...arr2];
const mySubArray = _.uniq(arr3, 'id');
console.log(mySubArray);
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.9.1/underscore-min.js"></script>


1

每个答案中都有这么多循环。

我的代码复杂度是2N,

思路如下:

  1. 合并数组。

  2. 第一个循环 - 标记重复项。

  3. 第二个循环 - 过滤掉重复项。

arr1 = [
  {id: 1, name: "a"},
  {id: 2, name: "b"},
  {id: 3, name: "c"},
  {id: 4, name: "d"},
];

arr2 = [
  {id: 1, name: "a"},
  {id: 4, name: "d"},
];

let newArray = [...arr1, ...arr2].reduce((acc, item, index) => {
  acc.items.push(item);

  if (typeof acc.map[item.id] !== 'undefined') {
    acc.items[acc.map[item.id]] = null;
    acc.items[index] = null;
  }
  acc.map[item.id] = index;
  
  return acc
},  {map: {}, items: []}).items.filter(item => !!item)


console.log(newArray);


代码的复杂度,我的答案是2N。很好的观点,如果数据集变得很大,并且性能成为问题,这样做是一个好主意。目前在这个小数据集上,它比简单的过滤器和一些东西慢了约8倍,但我认为在更大的数据集之前,这不会花费太多时间。我因此支持这个观点。我想,如果性能真的很重要,你甚至可以通过检查长度来同时执行两个操作.. :) - Keith

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