合并两个对象数组

202

让我们来看一个例子。

var arr1 = new Array({name: "lang", value: "English"},
                     {name: "age", value: "18"});

var arr2 = new Array({name : "childs", value: '5'},
                     {name: "lang", value: "German"});
我需要合并这两个对象数组,并创建以下数组:
var arr3 = new Array({name: "lang", value: "German"},
                     {name: "age", value: "18"},
                     {name : "childs", value: '5'});

有没有任何 JavaScript 或 jQuery 函数可以做到这一点?

$.extend 不适合我。它会返回

var arr4 = new Array({name : "childs", value: '5'},
                     {name: "lang", value: "German"});

29
“{name: "lang", value: "English"}”发生了什么? - Shadow The Spring Wizard
1
我的意思是,涉及到什么逻辑?你想如何合并这些数组?从你的示例中并不清楚。 - Shadow The Spring Wizard
13
@ShadowWizard,我认为OP的意思是如果存在就更新,如果不存在就推送。 - Amin Mohamed Ajani
2
注意:此问题在2018年被根本修改,当时它被编辑为两个“lang”条目都说“德语”。最初,一个说“英语”,另一个说“德语”。这改变了问题的含义,从“每个‘名称’仅保留一个元素”变为“除非所有属性都是重复的,否则保留元素”。我正在恢复该编辑,因为某些答案将不再有意义。这是为什么应该避免对问题进行重大编辑的例子。只需进行语法和样式更改,而不更改含义。那个意见应该是一个评论,而不是一个编辑 - ToolmakerSteve
@VasiliiSuricov 我不知道你在说什么。最好不要在问题中加入无关的内容,除非你只是在开玩笑,可以随时来我的聊天室(请查看我的个人资料),我们可以在那里讨论。祝好。 - undefined
显示剩余7条评论
47个回答

123

如果你想要在JavaScript中合并两个对象数组,你可以使用这行代码进行操作:

Array.prototype.push.apply(arr1,arr2);

例如:

var arr1 = [{name: "lang", value: "English"},{name: "age", value: "18"}];
var arr2 = [{name : "childs", value: '5'}, {name: "lang", value: "German"}];

Array.prototype.push.apply(arr1,arr2); 

console.log(arr1);  // final merged result will be in arr1

输出:

[{"name":"lang","value":"English"},
{"name":"age","value":"18"},
{"name":"childs","value":"5"},
{"name":"lang","value":"German"}]

31
与更简单的方法“arr1 = arr1.concat(arr2)”产生相同的结果。 - keithpjolley
@keithpjolley 如果你有两个合并的对象数组,使用“concat”无法获得上述输出。请查看此链接 https://jsfiddle.net/jahanzaibaslam/z22n5tuo/ - Jahanzaib Aslam
27
但这不是合并! - Dimitri Kopriwa
4
结果并不相同。concat方法会返回一个新数组,并打破之前的引用关系。 - Bpainter
38
这并没有回答问题,结果数组应该有3个对象,而不是4个。你改变了示例值。基本上,OP想要的是合并两个对象数组并删除重复值。 - Ranjan
显示剩余5条评论

72

使用ES6,您可以轻松完成如下操作:

var arr1 = new Array({name: "lang", value: "German"}, {name: "age", value: "18"});
var arr2 = new Array({name : "childs", value: '5'}, {name: "lang", value: "German"});
var arr3 = [...arr1, ...arr2];

输出:

    arr3 = [
      {"name":"lang","value":"German"},
      {"name":"age","value":"18"},
      {"name":"childs","value":"5"},
      {"name":"lang","value":"German"}
    ]

65
这并不是 OP 寻找的功能,不应该有第二个包含 { name: 'lang', value: 'German'} 的对象。 - William
9
我不知道为什么这个答案被投票了。@daveanderson88想要的与这个答案不同。 - Nguyễn Xuân Hoàng
1
不要使用 new Array( ... ),直接写 [ ... ] 更简单。 - Heretic Monkey

55

对于那些正在尝试现代技术的人:

var odd = [{
    name: "1",
    arr: "in odd"
  },
  {
    name: "3",
    arr: "in odd"
  }
];

var even = [{
    name: "1",
    arr: "in even"
  },
  {
    name: "2",
    arr: "in even"
  },
  {
    name: "4",
    arr: "in even"
  }
];

// ----
// ES5 using Array.filter and Array.find
function merge(a, b, prop) {
  var reduced = a.filter(function(aitem) {
    return !b.find(function(bitem) {
      return aitem[prop] === bitem[prop];
    });
  });
  return reduced.concat(b);
}
console.log("ES5", merge(odd, even, "name"));

// ----
// ES6 arrow functions
function merge(a, b, prop) {
  var reduced = a.filter(aitem => !b.find(bitem => aitem[prop] === bitem[prop]))
  return reduced.concat(b);
}
console.log("ES6", merge(odd, even, "name"));

// ----
// ES6 one-liner
var merge = (a, b, p) => a.filter(aa => !b.find(bb => aa[p] === bb[p])).concat(b);


console.log("ES6 one-liner", merge(odd, even, "name"));

// Results
// ( stuff in the "b" array replaces things in the "a" array )
// [
//    {
//         "name": "3",
//         "arr": "in odd"
//     },
//     {
//         "name": "1",
//         "arr": "in even"
//     },
//     {
//         "name": "2",
//         "arr": "in even"
//     },
//     {
//         "name": "4",
//         "arr": "in even"
//     }
// ]


// for posterity, here's the old skool version

function merge(a, b, prop) {
  var reduced = [];
  for (var i = 0; i < a.length; i++) {
    var aitem = a[i];
    var found = false;
    for (var ii = 0; ii < b.length; ii++) {
      if (aitem[prop] === b[ii][prop]) {
        found = true;
        break;
      }
    }
    if (!found) {
      reduced.push(aitem);
    }
  }
  return reduced.concat(b);
}


1
也许应该称之为“mergeOn”? - bob
1
我喜欢你的解决方案。ArrA将覆盖ArrB中的对象。不相交的对象将被连接到结果数组中。const mergeArrayAtoB = (arrA, arrB, property) => arrB.filter(b => !arrA.find(a => b[property] === a[property])).concat(arrA); - xeiton
1
兄弟,你真是救命恩人。谢谢你。 - jpisty

53

2019年10月12日更新

新版本仅基于更新的Javascript,并且无需任何第三方库。

const mergeByProperty = (target, source, prop) => {
  source.forEach(sourceElement => {
    let targetElement = target.find(targetElement => {
      return sourceElement[prop] === targetElement[prop];
    })
    targetElement ? Object.assign(targetElement, sourceElement) : target.push(sourceElement);
  })
}
var target /* arr1 */ = [{name: "lang", value: "English"}, {name: "age", value: "18"}];
var source /* arr2 */ = [{name : "childs", value: '5'}, {name: "lang", value: "German"}];

mergeByProperty(target, source, 'name');

console.log(target)

这篇回答已经有些过时了,像 lodash 和 underscore 这样的库现在已经不太需要了。 在这个新版本中,目标数组(arr1) 是我们正在使用并希望保持最新状态的数组。 源数组(arr2) 是新数据来自的地方,我们希望将其合并到我们的 目标 数组中。

我们遍历数组,寻找新数据,对于每个尚未在我们的目标数组中找到的对象,我们仅使用target.push(sourceElement)添加该对象。 如果基于我们的键属性('name'),一个对象已经存在于我们的目标数组中 - 我们使用Object.assign(targetElement, sourceElement)更新其属性和值。 我们的“目标”始终是同一个数组,并且具有更新的内容。


使用 underscore 或 lodash 的旧答案

我总是从谷歌上来到这里,但我总是对答案不满意。您的回答很好,但使用 underscore.js 会更简单、更整洁。

演示:http://jsfiddle.net/guya/eAWKR/

以下是一种更一般的函数,它将使用对象的属性合并2个数组。在这种情况下,属性是“name”

var arr1 = [{name: "lang", value: "English"}, {name: "age", value: "18"}];
var arr2 = [{name : "childs", value: '5'}, {name: "lang", value: "German"}];

function mergeByProperty(arr1, arr2, prop) {
  _.each(arr2, function(arr2obj) {
    var arr1obj = _.find(arr1, function(arr1obj) {
      return arr1obj[prop] === arr2obj[prop];
    });

    arr1obj ? _.extend(arr1obj, arr2obj) : arr1.push(arr2obj);
  });
}

mergeByProperty(arr1, arr2, 'name');

console.log(arr1);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.core.min.js"></script>

[{name: "lang", value: "German"}, {name: "age", value: "18"}, {name : "childs", value: '5'}]

6
除了@YOU的答案利用JS关联数组技巧实现O(1)查找功能外,几乎与其在LOC和复杂性上几乎相同,但这个答案要慢得多(主要是由于额外的函数调用以及需要引入一个额外的库)。请注意,翻译时需保持原意,并使内容更通俗易懂。 - Mrchief
除非你处理极端边缘情况,否则你不会得到任何性能差异。关于额外的库——underscore、lodash等通常已经被使用,并且有其他好处,应该考虑使用它。大多数开发人员和应用程序将从这种解决方案的简单性和普遍性中受益。 - guya
1
@guya 我知道这是老话题了,但是underscore、lodash等通常已经被使用并且有其他好处。虽然正如你所说它们有其他好处,但是假设它们通常已经被使用是一个巨大的假设。 - Craicerjack
1
新版本适用于Nodejs。谢谢,愿上帝保佑你! - Aniket Malik

43

非常简单,使用ES6扩展运算符:

const array1 = [{a: 'HI!'}, {b: 'HOW'}]
const array2 = [{c: 'ARE'}, {d: 'YOU?'}]

const mergedArray = [ ...array1, ...array2 ]
console.log('Merged Array: ', mergedArray)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

合并后的数组: [{a: 'HI!'}, {b: 'HOW'}, {c: 'ARE'}, {d: 'YOU?'}]

注: 上述解决方案只是使用ES6扩展运算符来合并两个数组。

由@bh4r4th于2020年1月7日编辑: 由于我的初始解决方案之后进行了编辑,上下文发生了变化。我想更新我的解决方案以匹配当前标准,即:

  1. 合并数组对象而不创建重复对象,并且

  2. 如果先前数组中的name属性已经存在,则更新value

const arr1 = [
    { name: "lang", value: "English" },
    { name: "age", value: "18" }
]
const arr2 = [
    { name: "childs", value: '2' }, 
    { name: "lang", value: "German" }
]
const arr3 = [
    { name: "lang", value: "German" },
    { name: "age", value: "28" },
    { name: "childs", value: '5' }
]

// Convert to key value dictionary or object
const convertToKeyValueDict = arrayObj => {
    const val = {}
    arrayObj.forEach(ob => {
        val[ob.name] = ob.value
    })
    return val
}

// update or merge array
const updateOrMerge = (a1, a2) => {
    const ob1 = convertToKeyValueDict(a1)
    const ob2 = convertToKeyValueDict(a2)
    // Note: Spread operator with objects used here
    const merged_obj = {...ob1, ...ob2}
    const val = Object.entries(merged_obj)
    return val.map(obj => ({ name: obj[0], value: obj[1] }))
}

const v1 = updateOrMerge(arr1, arr2)
const v2 = updateOrMerge(v1, arr3)
console.log(`Merged array1 and array2: ${JSON.stringify(v1)} \n\n`)
console.log(`Merged above response and array3: ${JSON.stringify(v2)} \n\n`)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>


2
这应该是2019年的最佳答案!在查看其他得票更高的答案后,我也打算提出同样的解决方案!@Alexander 很容易错过,但请接受这个答案! - iaforek
2
不正确。提问者特别想要替换掉具有重复属性的对象,而不仅仅是追加它们。 - HaveSpacesuit
感谢@HaveSpacesuit指出这一点。当时的情境只是合并两个数组,我在2018年已经回答了这个问题。很快就会编辑这个答案,使用set来合并并删除重复项。这将创建一个新的可变参数,但代码量很小。然而,我不太喜欢将名称定义为属性,值定义为不同类型的负载。相反,我更喜欢[ { lang: &#39;German&#39;, age: 25}] 。这样,在服务之间通信时,负载的重量将被最小化。 - bh4r4th
我现在已经更新了解决方案,以匹配问题的更改上下文。 - bh4r4th
工作得非常完美!这个答案值得更多的关注。 - Barlas Apaydin

27
var arr3 = [];
for(var i in arr1){
   var shared = false;
   for (var j in arr2)
       if (arr2[j].name == arr1[i].name) {
           shared = true;
           break;
       }
   if(!shared) arr3.push(arr1[i])
}
arr3 = arr3.concat(arr2);

在此输入图片描述


1
这个答案的时间复杂度很差(O(n^2))。使用哈希映射可以实现O(n)的解决方案。 - Alex von Brandenfels

20

合并两个数组:

var arr1 = new Array({name: "lang", value: "English"}, {name: "age", value: "18"});
var arr2 = new Array({name : "childs", value: '5'}, {name: "lang", value: "German"});
var result=arr1.concat(arr2);
// result: [{name: "lang", value: "English"}, {name: "age", value: "18"}, {name : "childs", value: '5'}, {name: "lang", value: "German"}]

合并两个数组并去除'name'重复的值:

var arr1 = new Array({name: "lang", value: "English"}, {name: "age", value: "18"});
var arr2 = new Array({name : "childs", value: '5'}, {name: "lang", value: "German"});
var i,p,obj={},result=[];
for(i=0;i<arr1.length;i++)obj[arr1[i].name]=arr1[i].value;
for(i=0;i<arr2.length;i++)obj[arr2[i].name]=arr2[i].value;
for(p in obj)if(obj.hasOwnProperty(p))result.push({name:p,value:obj[p]});
// result: [{name: "lang", value: "German"}, {name: "age", value: "18"}, {name : "childs", value: '5'}]

1
Array.concat是将两个数组合并成一个的正确方法。我还喜欢扩展假设,即针对每个数组元素的给定键,解决了“真正”合并的概念。 - bob

15

我想合并两个包含重复元素的数组,然后使用我的这个答案来删除重复项。这似乎是最简洁的方法。

const arr1 = [{
    name: "lang",
    value: "English"
  },
  {
    name: "age",
    value: "18"
  }
];

const arr2 = [{
    name: "childs",
    value: '5'
  },
  {
    name: "lang",
    value: "German"
  }
];

const mergedArray = [...arr1, ...arr2];
const uniqueData = [...mergedArray.reduce((map, obj) => map.set(obj.name, obj), new Map()).values()];

console.log(uniqueData)


14

最简单的方法是使用一些ES6魔法:

合并两个带有重复项的数组:

const a = [{a: 1}, {b: 2}]
const b = [{a: 1}]

const result = a.concat(b) // [{a: 1}, {b: 2}, {a: 1}]

没有重复项时,它与上面的结果相同,只需添加:

const distinct = [...new Set(result.map(item => item.YOUR_PROP_HERE))]


3
concat不能用于不同种类的项目。 - Phil

11

使用lodash:

_.uniqBy([...arr1, ...arr2], 'name')

ES6语法适用于两个数组,而不是两个对象数组。 - Dario

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