如何将两个对象数组唯一地合并?

9
我将尝试合并两个对象数组,但不使用lodash中的unionBy方法。
目前,我已经有了以下完美运行的代码:
var array1 = [
 { a: 1, b: 'first'},
 { a: 2, b: 'second'}
];

var array2 = [
 { a: 3, b: 'third'},
 { a: 1, b: 'fourth'}
];

var array3 = __.unionBy(array2, array1, 'a');

这将输出:
[
  {
    "a": 3,
    "b": "third"
  },
  {
    "a": 1,
    "b": "fourth"
  },
  {
    "a": 2,
    "b": "second"
  }
]

这是期望的结果,但我无法在当前的工作环境中使用unionBy,因此我正在寻找一个使用本机JS或其他lodash方法3.6.0或更低版本的结果。


1
你能否在你自己的代码中实现unionBy的基本功能呢? - Jason
6个回答

10

Concat和使用Array#filter与辅助对象一起去除重复项:

var array1 = [{"a":1,"b":"first"},{"a":2,"b":"second"}];

var array2 = [{"a":3,"b":"third"},{"a":1,"b":"fourth"}];

var result = array2.concat(array1).filter(function(o) {  
  return this[o.a] ? false : this[o.a] = true;
}, {});

console.log(result);

如果您可以使用ES6,那么可以使用Set而不是辅助对象:

const array1 = [{"a":1,"b":"first"},{"a":2,"b":"second"}];

const array2 = [{"a":3,"b":"third"},{"a":1,"b":"fourth"}];

const result = array2.concat(array1).filter(function(o) {  
  return this.has(o.a) ? false : this.add(o.a);
}, new Set());

console.log(result);

如果你想使用箭头函数,就不能使用Array.filter()thisArg来绑定Set作为函数的this(你不能将this绑定到箭头函数上)。相反,你可以使用一个闭包(方法的属性归功于@NinaScholz)。

const array1 = [{"a":1,"b":"first"},{"a":2,"b":"second"}];

const array2 = [{"a":3,"b":"third"},{"a":1,"b":"fourth"}];

const result = [...array2, ...array1]
  .filter((set => // store the set and return the actual callback
      o => set.has(o.a) ? false : set.add(o.a)
    )(new Set()) // use an IIFE to create a Set and store it set
  );

console.log(result);


运行完美,易于跟进! - zeckdude
有没有办法用箭头函数来编写这个代码?这里的 "this" 是指数组本身,对吧?虽然它能正常工作,但稍微解释一下会更好,谢谢。 - AbeIsWatching
1
this 是对象或 Set。Array.filter() 方法接受一个 thisArg,并将其绑定到回调函数。它不能是箭头函数。我将添加一个使用闭包的示例,其中使用了箭头函数和 Set。 - Ori Drori
“set => o =>” 这部分对我来说有些困惑,o 是这个集合成员的代表,但是这里的集合规则是什么?抱歉我有点新手。 - AbeIsWatching
噢,我明白了,这实际上是一个函数内部的另一个函数,你这样做是为了访问作为集合成员的对象的成员。set是第一个函数的参数,然后o是第二个函数的参数。 - AbeIsWatching

1
你可以使用 Set 进行过滤,以获取唯一值。

var array1 = [{ a: 1, b: 'first' }, { a: 2, b: 'second' }],
    array2 = [{ a: 3, b: 'third' }, { a: 1, b: 'fourth' }],
    s = new Set,
    array3 = array2.map(o => (s.add(o.a), o)).concat(array1.filter(o => !s.has(o.a)));

console.log(array3);


1
你可以使用ES6 Map来实现这个功能。用数据构造它,以a属性值为键,然后再从Map中取出值:

var array1 = [{"a":1,"b":"first"},{"a":2,"b":"second"}],
    array2 = [{"a":3,"b":"third"},{"a":1,"b":"fourth"}];

var result = [...new Map([...array1,...array2].map( o => [o.a, o] )).values()];

console.log(result);


0
你可以巧妙地使用 ES6 的 find 和 reduce 函数!

var array1 = [{"a":1,"b":"first"},{"a":2,"b":"second"}];
var array2 = [{"a":3,"b":"third"},{"a":1,"b":"fourth"}];
var res = array1.concat(array2).reduce((aggr, el)=>{
    if(!aggr.find(inst=>inst.a==el.a))
        return [...aggr, el];
    else
        return aggr
},[])

console.log(res);


0
你可以合并这两个数组,然后过滤掉那些具有相同属性 a 的元素:

var array1 = [{ a: 1, b: 'first'},{ a: 2, b: 'second'}],
  array2 = [{ a: 3, b: 'third'},{ a: 1, b: 'fourth'}],
  array3 = [...array2, ...array1].filter((item, pos, arr) =>
    arr.findIndex(item2 => item.a == item2.a) == pos);

console.log(array3)

如果你还想要能够指定属性进行合并,你可以实现自己的函数,像这样:

var array1 = [{ a: 1, b: 'first'},{ a: 2, b: 'second'}],
  array2 = [{ a: 3, b: 'third'},{ a: 1, b: 'fourth'}],
  array3 = unionBy(array1, array2, 'a');

function unionBy(array1, array2, prop){
  return [...array2, ...array1].filter((item, pos, arr) =>
      arr.findIndex(item2 => item[prop] == item2[prop]) == pos);
}

console.log(array3);

注意:我的答案相比其他答案的优势在于它保留了类似于lodash中的顺序,这可能很重要。

0

使用 Array.filterArray.find 的 ES5

var array1 = [{ a: 1, b: "first" }, { a: 2, b: "second" }];

var array2 = [{ a: 3, b: "third" }, { a: 1, b: "fourth" }];

function merge(a, b, prop) {
  var reduced = a.filter(function(itemA) {
return !b.find(function(itemB) {
  return itemA[prop] === itemB[prop];
});
  });
  return reduced.concat(b);
}

console.log(merge(array1, array2, "a"));

ES6 arrow functions

var array1 = [{ a: 1, b: "first" }, { a: 2, b: "second" }];

var array2 = [{ a: 3, b: "third" }, { a: 1, b: "fourth" }];

function merge(a, b, prop) {
  const reduced = a.filter(
itemA => !b.find(itemB => itemA[prop] === itemB[prop])
  );
  return reduced.concat(b);
}

console.log(merge(array1, array2, "a"));

Another ES6 one line experiment

var array1 = [{ a: 1, b: "first" }, { a: 2, b: "second" }];

var array2 = [{ a: 3, b: "third" }, { a: 1, b: "fourth" }];

const merge = (a, b, p) => a.filter( aa => ! b.find ( bb => aa[p] === bb[p]) ).concat(b);

console.log(merge(array1, array2, "a"));


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