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个回答

1
和 @gevorg 的回答一样,但如果没有找到匹配项,您可能还希望将一个新对象添加到原始数组中。
let combinedEvents = origEvents;
for(let i =0; i< newEvents.length; i++){
  let newEvent = newEvents[i];
  for(let j =0; j< origEvents.length; j++){
    let origEvent = origEvents[j];
    if(newEvent.events_id == origEvent.events_id){
      combinedEvents.splice(j,1, newEvent);
      break;
    } else if(j === origEvents.length - 1){
      combinedEvents.push(newEvent);
      break;
    }
  }
}

0

Backbone的集合非常适合这种情况。

首先,我们定义一个模型类型,它知道如何查找name属性:

import { Model } from 'backbone';

const President = Model.extend({ idAttribute: 'name' });

然后,我们将初始数组放入使用上述模型的集合中:

import { Collection } from 'backbone';

const presidents = new Collection(origArr, { model: President });

现在,我们可以随时运行presidents.set,它会将新的表示与旧的值合并:
presidents.set(updateArr);

presidents.get('Cruz').get('isRunning'); // false
presidents.toJSON(); // NEWArr

将所有内容放在一个可运行的代码片段中:

const { Model, Collection } = Backbone;

const President = Model.extend({ idAttribute: 'name' });

const presidents = new Collection([
  {name: 'Trump', isRunning: true},
  {name: 'Cruz', isRunning: true},
  {name: 'Kasich', isRunning: true}
], { model: President });

presidents.set([
  {name: 'Cruz', isRunning: false},
  {name: 'Kasich', isRunning: false}
]);

console.log(presidents.get('Cruz').get('isRunning'));
console.log(presidents.toJSON());
<script src="https://underscorejs.org/underscore-umd-min.js"></script>
<script src="https://backbonejs.org/backbone-min.js"></script>


单行纯下划线解决方案在此:https://dev59.com/OFoU5IYBdhLWcg3w1pX2#70474201 - Julian

0
虽然Underscore没有Lodash的unionBy的直接等价物,但是你仍然可以使用一个高效的一行代码,通过concat、groupBy、map和last获得相同的结果:
import { groupBy, map, last } from 'underscore';

var NEWArr = map(groupBy(origArr.concat(updateArr), 'name'), last);

使用chain的等效,略微冗长但更清晰的表示法:

import { chain } from 'underscore';

var NEWArr = chain(origArr).concat(updateArr).groupBy('name').map(last).value();

事实上,您可以使用上述方法定义自己的unionBy并将其添加到Underscore中:
import _, { groupBy, map, first } from 'underscore';

function unionBy(...args) {
    const iteratee = _.iteratee(args.pop());
    const candidates = [].concat.apply(null, args);
    return map(groupBy(candidates, iteratee), first);
}

_.mixin({ unionBy }); // unionBy can now be used in chaining

请注意,我们现在使用first而不是last,因为Lodash的unionBy语义规定第一次出现的获胜。

虽然原问题没有提到,但是Backbone无疑拥有最好的数据更新解决方案。请参见https://dev59.com/OFoU5IYBdhLWcg3w1pX2#70474027。 - Julian

-2
尝试使用 ES-6 Set 数据结构来实现这个方法: const result = [...new Set([...origArr, ...updatingArr])]

这只适用于平面列表。对于这种情况不起作用。 - bharadhwaj

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