JavaScript - 合并两个对象数组并根据属性值去重

18

我想用另一个数组中的对象替换我的数组中的对象。每个对象具有相同的结构,例如:

var origArr = [
  {name: 'Trump', isRunning: true},
  {name: 'Cruz', isRunning: true},
  {name: 'Kasich', isRunning: true}
];
var updatingArr = [
  {name: 'Cruz', isRunning: false},
  {name: 'Kasich', isRunning: false}
];
// desired result:
NEWArr = [
  {name: 'Trump', isRunning: true},
  {name: 'Cruz', isRunning: false},
  {name: 'Kasich', isRunning: false}
];
我尝试了concat()和Underscore的_.uniq函数,但它总是丢弃更新后的对象并返回原始数组。是否有一种方法可以通过匹配name属性,在origArr中用updatingArr的对象覆盖(替换)它?

1
这是您的意思吗?https://jsfiddle.net/4p98w3su/ 不确定我是否理解了问题。 - spaceman
这里有很多好的回答;我喜欢您使用splice的最佳答案。如果您想将其转换为答案,我会标记为已接受。谢谢。 - Sean O
14个回答

20
我来到这里就是为了找到这个,看到@Gruff Bunny的技巧,想知道“lodash”是否比“underscore”更好?

结果发现:
let result = _.unionBy(updatingArr, origArr, 'name');

虽然Underscore比Lodash小得多,但是您仍然可以使用concatgroupBymaplast在一行代码中获得相同的结果。您还可以在只有五行代码的情况下将unionBy添加到Underscore中。请参见此答案 - Julian

10
你可以使用 Array#map 结合 Array#reduce 来实现。

var origArr = [{ name: 'Trump', isRunning: true }, { name: 'Cruz', isRunning: true }, { name: 'Kasich', isRunning: true }],
    updatingArr = [{ name: 'Cruz', isRunning: false }, { name: 'Kasich', isRunning: false }],
    NEWArr = origArr.map(function (a) {
        return this[a.name] || a;
    }, updatingArr.reduce(function (r, a) {
        r[a.name] = a;
        return r;
    }, Object.create(null)));

document.write('<pre>' + JSON.stringify(NEWArr, 0, 4) + '</pre>');

更新 2022

使用一个带有 name 属性的对象作为哈希表,并通过从哈希表或原始对象中获取更新来映射原始数组。

const
    origArr = [{ name: 'Trump', isRunning: true }, { name: 'Cruz', isRunning: true }, { name: 'Kasich', isRunning: true }],
    updatingArr = [{ name: 'Cruz', isRunning: false }, { name: 'Kasich', isRunning: false }],
    updates = Object.fromEntries(updatingArr.map(o => [o.name, o])),
    result = origArr.map(o => updates[o.name] || o);

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

另一种方法是使用Map

这种方法也适用于只在更新数组中的对象。

const
    origArr = [{ name: 'Trump', isRunning: true }, { name: 'Cruz', isRunning: true }, { name: 'Kasich', isRunning: true }],
    updatingArr = [{ name: 'Cruz', isRunning: false }, { name: 'Kasich', isRunning: false }],
    result = Array.from([...origArr, ...updatingArr]
        .reduce((m, o) => m.set(o.name, o), new Map)
        .values()
    );

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


我需要一些解释,请... :/ - A. Rabus
@A.Rabus,这段代码实际上使用哈希表来更新值,并将更新的值或原始值映射。实际上,我会将这两个步骤分成两部分,并且不再使用this - Nina Scholz
如何实现:如果从updatingArr id在origArr中不存在,则只需将updatingArr元素推送到origArr。 - user5392173
太棒了!恭喜你! - Fred Hors

8
使用双重循环和splice函数,您可以像这样完成它:
for(var i = 0, l = origArr.length; i < l; i++) {
    for(var j = 0, ll = updatingArr.length; j < ll; j++) {
        if(origArr[i].name === updatingArr[j].name) {
            origArr.splice(i, 1, updatingArr[j]);
            break;
        }
    }
}

Example here


如果更新数组(updatingArr)中的id在原始数组(origArr)中不存在,则将更新数组元素推送到原始数组中,以此实现该答案的接受。 - user5392173

3
const origArr = [
  {name: 'Trump', isRunning: true},
  {name: 'Cruz', isRunning: true},
  {name: 'Kasich', isRunning: true}
];

const updatingArr = [
  {name: 'Cruz', isRunning: false},
  {name: 'Kasich', isRunning: false}
];

let hash = {};

for(let i of origArr.concat(updatingArr)) {
  if(!hash[i]) {
    hash[i.name] = i;
  }
}

let newArr = [];

for(let i in hash) {
  newArr.push(hash[i])
}

console.log(newArr);

2

这个版本允许你定义选择器来定义一个对象是否为重复的。

  • forEach迭代新数据
  • findIndex返回一个索引>= 0,如果两个选择器相等。如果没有相等的,则返回-1
  • 如果有重复,我们使用slice来用新的替换原来的。
  • 如果没有重复,我们将其推入原始数组中。

const origArr = [
  {name: 'Trump', isRunning: true},
  {name: 'Cruz', isRunning: true},
  {name: 'Kasich', isRunning: true}
];

const updatingArr = [
  {name: 'Cruz', isRunning: false},
  {name: 'Kasich', isRunning: false}
];

const mergeArrayOfObjects = (original, newdata, selector = 'key') => {
 newdata.forEach(dat => {
  const foundIndex = original.findIndex(ori => ori[selector] == dat[selector]);
  if (foundIndex >= 0) original.splice(foundIndex, 1, dat);
        else original.push(dat);
 });

 return original;
};

const result = mergeArrayOfObjects(origArr, updatingArr, "name")
console.log('RESULT -->', result)


2
您可以使用哈希表根据名称获取索引,再使用Object.assign进行更新。
var hash = origArr.reduce(function(hash, obj, index) {
  hash[obj.name] = index;
  return hash;
}, Object.create(null));
for(var obj of updatingArr) {
  Object.assign(origArr[hash[obj.name]], obj);
}

2
你可以试一下这个。
var origArr = [
  {name: 'Trump', isRunning: true},
  {name: 'Cruz', isRunning: true},
  {name: 'Kasich', isRunning: true}
];
var updatingArr = [
  {name: 'Cruz', isRunning: false},
  {name: 'Kasich', isRunning: false}
];

var origLength = origArr.length;
var updatingLength = updatingArr.length;

//Traverse the original array and replace only if the second array also has the same value
for(i = origLength-1; i >= 0; i--) {
    for(j = updatingLength -1; j >= 0; j--) {
    if(origArr[i].name === updatingArr[j].name) {
        origArr[i] = updatingArr[j];
    }
  }
}

console.log(origArr);

根据jspref进行的测试,遍历以相反顺序进行,因为这是最省时间的。 - Manish M Demblani

2

这里是使用underscore的解决方案:

var result = _.map(origArr, function(orig){
    return _.extend(orig, _.findWhere(updatingArr, {name: orig.name}));
});

使用Underscore的更紧凑和高效的解决方案,还可以在更新时添加新条目:https://dev59.com/OFoU5IYBdhLWcg3w1pX2#70474201 - Julian

2
在ES6中,您可以像这样使用对象Map...
let map = new Map();
let origArr = [
  {name: 'Trump', isRunning: true},
  {name: 'Cruz', isRunning: true},
  {name: 'Kasich', isRunning: true}
];
let updatingArr = [
  {name: 'Cruz', isRunning: false},
  {name: 'Kasich', isRunning: false}
];

// Concating arrays with duplicates
let NEWArr = origArr.concat(updatingArr);

// Removing duplicates items
NEWArr.forEach(item => {
  if(!map.has(item.name)){
    map.set(item.name, item);
  }
});

Array.from(map.values());

注意:Map对象需要唯一的键,在这种情况下,我使用了 name 作为键。


1
谢谢,这很简洁明了! - amos
我不认为这是正确的。因为结果是 {name: 'Trump', isRunning: true}, {name: 'Cruz', isRunning: true}, {name: 'Kasich', isRunning: true},我认为这不是你想要的。 - Fred Hors

1
这将做到您所需的:

var origArr = [
  {name: 'Trump', isRunning: true},
  {name: 'Cruz', isRunning: true},
  {name: 'Kasich', isRunning: true}
];

var updatingArr = [
  {name: 'Cruz', isRunning: false},
  {name: 'Kasich', isRunning: false}
];

for (var i = 0; i < updatingArr.length; ++i) {
  var updateItem = updatingArr[i];
  for (var j = 0; j < origArr.length; ++j) {
    var origItem = origArr[j];
    if (origItem.name == updateItem.name) {
      origItem.isRunning = updateItem.isRunning;
      break;    
    }
  }
}

document.write('<pre>' + JSON.stringify(origArr, 0, 4) + '</pre>');


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