Javascript:合并两个对象数组,前提是不重复(基于指定的对象键) 背景

Javascript:合并两个对象数组,前提是不重复(基于指定的对象键) 背景,javascript,arrays,Javascript,Arrays,假设我有一个对象的初始数组: var initialData = [ { 'ID': 1, 'FirstName': 'Sally' }, { 'ID': 2, 'FirstName': 'Jim' }, { 'ID': 3, 'FirstName': 'Bob' } ]; var newData = [ { 'ID': 2,

假设我有一个对象的初始数组:

var initialData = [
    {
        'ID': 1,
        'FirstName': 'Sally'
    },
    {
        'ID': 2,
        'FirstName': 'Jim'
    },
    {
        'ID': 3,
        'FirstName': 'Bob'
    }
];
var newData = [
    {
        'ID': 2,
        'FirstName': 'Jim'
    },
    {
        'ID': 4,
        'FirstName': 'Tom'
    },
    {
        'ID': 5,
        'FirstName': 'George'
    }
];
然后获取新数据(另一个对象数组):

目标 我想将新数据合并到初始数据中。但是,我不想覆盖初始数据数组中的任何对象。我只想添加一些不存在的对象

根据对象的
'ID'
键,我知道这些对象是重复的

我试过的 我知道我可以通过循环新数据来实现这一点,检查初始数据中是否存在该数据,如果不存在,则推入初始数据

for ( var i = 0, l = newData.length; i < l; i++  ) {

    if ( ! key_exists( newData[i].key, initialData ) ) {  // key_exists() is a function that uses .filter() to test.

        initialData.push( newData[i] );

    }


}
for(变量i=0,l=newData.length;i
不过,我关心的是性能。我知道有很多新的ES6操作数组的方法,所以我希望有人有更好的想法

问题:
在忽略新数据中的重复项的同时,将新数据合并到初始数据中的最佳方式(最佳性能)是什么?

您可以从
initialData
创建一组ID,这将使“检查ID是否已在初始数据中”更快-O(1):

var initialData=[{
“ID”:1,
“名字”:“莎莉”
},
{
“ID”:2,
“名字”:“吉姆”
},
{
"ID":3,,
“名字”:“鲍勃”
}
];
var newData=[{
“ID”:2,
“名字”:“吉姆”
},
{
“ID”:4,
“名字”:“汤姆”
},
{
“ID”:5,
“名字”:“乔治”
}
];
var ID=新集合(initialData.map(d=>d.ID));
var merged=[…initialData,…newData.filter(d=>!ids.has(d.ID))];

console.log(合并)事实上,如果您对性能感兴趣,您可以考虑将
initialData
结构更改为以下内容:

var initialData = {
    "1": {'FirstName': 'Sally'},
    "2": {'FirstName': 'Jim'},
    "3": {'FirstName': 'Bob'}
};
换句话说,我们使用ID作为对象的键,这将为您提供访问数据的
O(1)
,以及现有测试中的
O(1)
。您可以使用下一种方法获得此结构,方法如下:

var initialData=[
{'ID':1,'FirstName':'Sally'},
{'ID':2,'FirstName':'Jim'},
{'ID':3,'FirstName':'Bob'}
];
让newInitialData=initialData.reduce((res,{ID,FirstName})=>
{
res[ID]={FirstName:FirstName};
返回res;
}, {});

console.log(newInitialData)我认为@slider在接受的答案中提出的替代解决方案是这样的:

var initialData = {
    "1": {'FirstName': 'Sally'},
    "2": {'FirstName': 'Jim'},
    "3": {'FirstName': 'Bob'}
};
constolddata=[
{id:1,名字:'John'},
{id:2,名字:'Jane'},
{id:3,名字:'Mike'},
];
常数newData=[
{id:2,名字:'Jane'},
{id:4,名字:'Rick'},
{id:5,名字:'Jim'},
];
const usersMap=新映射();
forEach(user=>usersMap.set(user.id,user));
newData.forEach(用户=>{
const exists=usersMap.has(user.id);
如果(!存在){
usersMap.set(user.id,user);
}
})

usersMap.forEach(user=>console.log(user))
这不是一个真正的重复问题,因为在这个问题中,当有两个重复的对象时,它们需要合并。在我的例子中,当存在重复的对象时,我想删除新的对象。您是否保证对ID进行任何排序?这可以减少循环,因为您可以优化搜索,并根据当前对象的
id
做出决策。即使没有,也可以在循环之前应用简单的排序。@尼克,我不能保证排序。99%的情况下,顺序会有所不同。我似乎标记了错误的,我不得不忍住,所以我投票重新开始,但这肯定是在前面的问题中彻底讨论过的理由。通常的解决方案是构建贴图(或对象作为贴图)。我只是手头没有一个例子,不得不消失。@t.J.Crowder,我在发布之前确实查看了现有的问题,但我发现的所有问题都只是合并了重复的对象,而不是删除重复的对象。不过,我可能错过了。如果是这样的话,我表示歉意,谢谢你帮我做标记。尼特:另一个可能的性能改进是使用concat,而不是传播。有消息说concat的速度快了一点。谢谢关于您的回答的后续问题,创建
集合而不是
ID的
数组
的原因是什么?@AndyMercer使用
集合
,检查ID是否在其中(
ID.has(d.ID)
)是恒定时间。而在数组中,则是线性时间(
包含
过滤器
必须遍历列表中的每个元素)。在这种情况下,
Set
的这一优势对于提高效率至关重要。简洁的回答很好,但我花了一段时间才弄明白为什么它没有合并数据!我发现我必须
返回
过滤器上的回调
varids=newset(initialData.map(d=>d.ID));var merged=[…initialData,…newData.filter(d=>return!ids.has(d.ID))];console.log(合并)
@slider感谢大家对
{}
的关注。我正在处理的代码有一个稍微不同的流程,我的合并数组缺少所有新数据,直到我返回过滤器箭头函数,因为我的代码有大括号!总是在学习。我喜欢这样,但我不确定这会如何影响我正在做的排序(这超出了最初问题的范围)。我比较频繁地使用
Array.sort()
,如果我将数组变为对象,这将不起作用(我认为)。对象无法排序,您必须在排序之前将其再次转换为数组。与一般规则一样,您的数据结构应该针对您更经常使用的操作类型进行优化。