根据一个键合并两个对象数组

我有两个数组:

数组1:

[
{ id: "abdc4051", date: "2017-01-24" },
{ id: "abdc4052", date: "2017-01-22" }
]

和数组2:

[
{ id: "abdc4051", name: "ab" },
{ id: "abdc4052", name: "abc" }
]

我需要根据 id合并这两个数组,得到如下结果:

[
{ id: "abdc4051", date: "2017-01-24", name: "ab" },
{ id: "abdc4052", date: "2017-01-22", name: "abc" }
]

我如何做到这一点,而不迭代槽 Object.keys

201193 次浏览

你可以这样做

let arr1 = [
{ id: "abdc4051", date: "2017-01-24" },
{ id: "abdc4052", date: "2017-01-22" }
];


let arr2 = [
{ id: "abdc4051", name: "ab" },
{ id: "abdc4052", name: "abc" }
];


let arr3 = arr1.map((item, i) => Object.assign({}, item, arr2[i]));


console.log(arr3);


如果 arr1arr2的顺序不同,请使用以下代码:

let arr1 = [
{ id: "abdc4051", date: "2017-01-24" },
{ id: "abdc4052", date: "2017-01-22" }
];


let arr2 = [
{ id: "abdc4051", name: "ab" },
{ id: "abdc4052", name: "abc" }
];


let merged = [];


for(let i=0; i<arr1.length; i++) {
merged.push({
...arr1[i],
...(arr2.find((itmInner) => itmInner.id === arr1[i].id))}
);
}


console.log(merged);

如果 arr1arr2的顺序相同,则使用此选项

let arr1 = [
{ id: "abdc4051", date: "2017-01-24" },
{ id: "abdc4052", date: "2017-01-22" }
];


let arr2 = [
{ id: "abdc4051", name: "ab" },
{ id: "abdc4052", name: "abc" }
];


let merged = [];


for(let i=0; i<arr1.length; i++) {
merged.push({
...arr1[i],
...arr2[i]
});
}


console.log(merged);

假设两个数组的长度相同,我可能会这样做:

var newArr = []
for (var i = 0; i < array1.length; i++ {
if (array1[i].id === array2[i].id) {
newArr.push({id: array1[i].id, date: array1[i].date, name: array2[i].name});
}
}

可以使用数组方法

let arrayA=[
{id: "abdc4051", date: "2017-01-24"},
{id: "abdc4052", date: "2017-01-22"}]


let arrayB=[
{id: "abdc4051", name: "ab"},
{id: "abdc4052", name: "abc"}]


let arrayC = [];




  



arrayA.forEach(function(element){
arrayC.push({
id:element.id,
date:element.date,
name:(arrayB.find(e=>e.id===element.id)).name
});
});


console.log(arrayC);


//0:{id: "abdc4051", date: "2017-01-24", name: "ab"}
//1:{id: "abdc4052", date: "2017-01-22", name: "abc"}

您可以递归地将它们合并为一个,如下所示:

function mergeRecursive(obj1, obj2) {
for (var p in obj2) {
try {
// Property in destination object set; update its value.
if (obj2[p].constructor == Object) {
obj1[p] = this.mergeRecursive(obj1[p], obj2[p]);


} else {
obj1[p] = obj2[p];


}


} catch (e) {
obj1[p] = obj2[p];


}
}
return obj1;
}


arr1 = [
{ id: "abdc4051", date: "2017-01-24" },
{ id: "abdc4052", date: "2017-01-22" }
];
arr2 = [
{ id: "abdc4051", name: "ab" },
{ id: "abdc4052", name: "abc" }
];


mergeRecursive(arr1, arr2)
console.log(JSON.stringify(arr1))

您可以使用任意的数组计数并映射到相同的索引新对象。

var array1 = [{ id: "abdc4051", date: "2017-01-24" }, { id: "abdc4052", date: "2017-01-22" }],
array2 = [{ id: "abdc4051", name: "ab" }, { id: "abdc4052", name: "abc" }],
result = [array1, array2].reduce((a, b) => a.map((c, i) => Object.assign({}, c, b[i])));
    

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

你可以在一行中做到这一点

let arr1 = [
{ id: "abdc4051", date: "2017-01-24" },
{ id: "abdc4052", date: "2017-01-22" }
];


let arr2 = [
{ id: "abdc4051", name: "ab" },
{ id: "abdc4052", name: "abc" }
];


const mergeById = (a1, a2) =>
a1.map(itm => ({
...a2.find((item) => (item.id === itm.id) && item),
...itm
}));


console.log(mergeById(arr1, arr2));

  1. 数组上的映射1
  2. 在 array2中搜索 array1.id
  3. 如果您找到了它... ... 将 array2的结果扩展到 array1

最后一个数组将只包含与两个数组匹配的 id

我能够通过两个数组的嵌套映射和更新初始数组来实现这一点:

member.map(mem => {
return memberInfo.map(info => {
if (info.id === mem.userId) {
mem.date = info.date;
return mem;
}
}
}

要合并 id上的两个数组,假设这两个数组的长度相等:

arr1.map(item => ({
...item,
...arr2.find(({ id }) => id === item.id),
}));

这些解决方案都不适用于我的情况:

  • 缺少的对象可以存在于任一数组中
  • O (n)的运行时复杂度

附注:

  • 我使用了 loash,但它很容易替换为其他东西
  • 也使用了类型脚本(只需删除/忽略类型)
import { keyBy, values } from 'lodash';


interface IStringTMap<T> {
[key: string]: T;
}


type IIdentified = {
id?: string | number;
};


export function mergeArrayById<T extends IIdentified>(
array1: T[],
array2: T[]
): T[] {
const mergedObjectMap: IStringTMap<T> = keyBy(array1, 'id');


const finalArray: T[] = [];


for (const object of array2) {
if (object.id && mergedObjectMap[object.id]) {
mergedObjectMap[object.id] = {
...mergedObjectMap[object.id],
...object,
};
} else {
finalArray.push(object);
}
}


values(mergedObjectMap).forEach(object => {
finalArray.push(object);
});


return finalArray;
}

下面是一个 O (n)解决方案,使用 reduce 和 Object.sign

const joinById = ( ...lists ) =>
Object.values(
lists.reduce(
( idx, list ) => {
list.forEach( ( record ) => {
if( idx[ record.id ] )
idx[ record.id ] = Object.assign( idx[ record.id ], record)
else
idx[ record.id ] = record
} )
return idx
},
{}
)
)

为了在 OP 的情况下使用这个函数,传入您想要加入到 join ById 的数组(注意 list 是一个 rest 参数)。

let joined = joinById(list1, list2)

每个列表被简化为一个对象,其中键是 id,值是对象。如果已经在给定键处有一个值,那么它将获得对其调用的 object.sign 和当前记录。

这是通用的 O (n * m)解决方案,其中 n 是记录的数量,m 是键的数量。这只适用于有效的对象键。您可以将任何值转换为 base64,并在需要时使用它。

const join = ( keys, ...lists ) =>
lists.reduce(
( res, list ) => {
list.forEach( ( record ) => {
let hasNode = keys.reduce(
( idx, key ) => idx && idx[ record[ key ] ],
res[ 0 ].tree
)
if( hasNode ) {
const i = hasNode.i
Object.assign( res[ i ].value, record )
res[ i ].found++
} else {
let node = keys.reduce( ( idx, key ) => {
if( idx[ record[ key ] ] )
return idx[ record[ key ] ]
else
idx[ record[ key ] ] = {}
return idx[ record[ key ] ]
}, res[ 0 ].tree )
node.i = res[ 0 ].i++
res[ node.i ] = {
found: 1,
value: record
}
}
} )
return res
},
[ { i: 1, tree: {} } ]
)
.slice( 1 )
.filter( node => node.found === lists.length )
.map( n => n.value )

除了它保留一个 index 对象来标识要连接的记录之外,这个方法本质上与 joById 方法相同。这些记录存储在一个数组中,索引存储给定键集的记录位置以及它所在的列表数。

每次遇到相同的键集时,它都会在树中找到节点,更新其索引处的元素,并且找到的次数会递增。

加入之后,idx 对象将随切片一起从数组中删除,并且删除每个集合中未找到的所有元素。这使它成为一个内部连接,您可以删除这个过滤器,并有一个完整的外部连接。

最后,将每个元素映射到它的值,并且您将得到连接的数组。

如果你有2个数组需要合并的基础上的价值,甚至它在不同的顺序

let arr1 = [
{ id:"1", value:"this", other: "that" },
{ id:"2", value:"this", other: "that" }
];


let arr2 = [
{ id:"2", key:"val2"},
{ id:"1", key:"val1"}
];

你可以这样做

const result = arr1.map(item => {
const obj = arr2.find(o => o.id === item.id);
return { ...item, ...obj };
});


console.log(result);

即使合并的数组具有不同的大小,此解决方案也是适用的。 此外,即使匹配的键有不同的名称。

使用 Map 合并两个数组,如下所示:

const arr1 = [
{ id: "abdc4051", date: "2017-01-24" },
{ id: "abdc4052", date: "2017-01-22" },
{ id: "abdc4053", date: "2017-01-22" }
];
const arr2 = [
{ nameId: "abdc4051", name: "ab" },
{ nameId: "abdc4052", name: "abc" }
];


const map = new Map();
arr1.forEach(item => map.set(item.id, item));
arr2.forEach(item => map.set(item.nameId, {...map.get(item.nameId), ...item}));
const mergedArr = Array.from(map.values());


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

Run the stack snippet to see the result:

[
{
"id": "abdc4051",
"date": "2017-01-24",
"nameId": "abdc4051",
"name": "ab"
},
{
"id": "abdc4052",
"date": "2017-01-22",
"nameId": "abdc4052",
"name": "abc"
},
{
"id": "abdc4053",
"date": "2017-01-22"
}
]

这是一个版本,当你有一个对象和一个数组,你想要合并它们,并给数组一个键值,以便它很好地适合对象。

var fileData = [
{ "id" : "1", "filename" : "myfile1", "score" : 33.1 },
{ "id" : "2", "filename" : "myfile2", "score" : 31.4 },
{ "id" : "3", "filename" : "myfile3", "score" : 36.3 },
{ "id" : "4", "filename" : "myfile4", "score" : 23.9 }
];


var fileQuality = [0.23456543,0.13413131,0.1941344,0.7854522];


var newOjbect = fileData.map((item, i) => Object.assign({}, item, {fileQuality:fileQuality[i]}));


console.log(newOjbect);

我们可以在这里使用 loash. _. merge 按照您的预期工作。它使用现有的公共键。

_.merge(array1, array2)

不管合并顺序如何,

function merge(array,key){
let map = {};
array.forEach(val=>{
if(map[val[key]]){
map[val[key]] = {...map[val[key]],...val};
}else{
map[val[key]] = val;
}
})
return Object.keys(map).map(val=>map[val]);
}


let b = [
{ id: "abdc4051", name: "ab" },
{ id: "abdc4052", name: "abc" }
];
let a = [
{ id: "abdc4051", date: "2017-01-24" },
{ id: "abdc4052", date: "2017-01-22" }
];


console.log(merge( [...a,...b], 'id'));

下面是一行程序(数组中元素的顺序并不重要,假设存在1到1的关系) :

var newArray = array1.map(x=>Object.assign(x, array2.find(y=>y.id==x.id)))

我迭代了第一个数组,并在第二个数组上使用 .find方法查找与 id相等的匹配项并返回结果。

const a = [{ id: "abdc4051", date: "2017-01-24" },{ id: "abdc4052", date: "2017-01-22" }];
const b = [{ id: "abdc4051", name: "ab" },{ id: "abdc4052", name: "abc" }];


console.log(a.map(itm => ({...itm, ...b.find(elm => elm.id == itm.id)})));

如果两个数组都有非相交项,则使用的方法。

const firstArray = [
{ id: 1, name: "Alex", salutation: "Mr." },
{ id: 2, name: "Maria", salutation: "Ms." },
];


const secondArray = [
{ id: 2, address: "Larch Retreat 31", postcode: "123452" },
{ id: 3, address: "Lycroft Close 12D", postcode: "123009" },
];


const mergeArr = (arr1, arr2) => {
const obj = {};


arr1.forEach(item => {
obj[item.id] = item;
});


arr2.forEach(item => {
obj[item.id]
? (obj[item.id] = { ...obj[item.id], ...item })
: (obj[item.id] = item);
});


return Object.values(obj);
};


const output = mergeArr(firstArray, secondArray);


console.log(output);

有很多解决方案,但是,我们可以简单地使用 for循环和 if条件得到合并数组。

const firstArray = [
{ id: 1, name: "Alex", salutation: "Mr." },
{ id: 2, name: "Maria", salutation: "Ms." },
];


const secondArray = [
{ id: 1, address: "Larch Retreat 31", postcode: "123452" },
{ id: 2, address: "Lycroft Close 12D", postcode: "123009" },
];


let mergedArray: any = [];


for (const arr1 of firstArray) {
for (arr2 doc of secondArray) {
if (arr1.id === arr2.id) {
mergedArray.push({ ...arr1, ...arr2 });
}
}
}


console.log(mergedArray)

Python3解决方案的人谁登陆这个页面,希望找到一个

def merge(studentDetails, studentMark, merge_key):
student_details = {}
student_marks = {}
for sd, sm in zip(studentDetails, studentMark):
key = sd.pop(merge_key)
student_details[key] = sd


key = sm.pop(merge_key)
student_marks[key] = sm


res = []
for id, val in student_details.items():
# Merge three dictionary together
temp = {**{"studentId": id}, **val, **student_marks[id]}
res.append(temp)
return res




if __name__ == '__main__':
# Test Case 1
studentDetails = [
{"studentId": 1, "studentName": 'Sathish', "gender": 'Male', "age": 15},
{"studentId": 2, "studentName": 'kumar', "gender": 'Male', "age": 16},
{"studentId": 3, "studentName": 'Roja', "gender": 'Female', "age": 15},
{"studentId": 4, "studentName": 'Nayanthara', "gender": 'Female', "age": 16},
]
studentMark = [
{"studentId": 1, "mark1": 80, "mark2": 90, "mark3": 100},
{"studentId": 2, "mark1": 80, "mark2": 90, "mark3": 100},
{"studentId": 3, "mark1": 80, "mark2": 90, "mark3": 100},
{"studentId": 4, "mark1": 80, "mark2": 90, "mark3": 100},
]


# Test Case 2
array1 = [
{"id": "abdc4051", "date": "2017-01-24"},
{"id": "abdc4052", "date": "2017-01-22"}
]
array2 = [
{"id": "abdc4051", "name": "ab"},
{"id": "abdc4052", "name": "abc"}
]


output = merge(studentDetails, studentMark, merge_key="studentId")
[print(a) for a in output]


output = merge(array1, array2, merge_key="id")
[print(a) for a in output]

输出

{'studentId': 1, 'studentName': 'Sathish', 'gender': 'Male', 'age': 15, 'mark1': 80, 'mark2': 90, 'mark3': 100}
{'studentId': 2, 'studentName': 'kumar', 'gender': 'Male', 'age': 16, 'mark1': 80, 'mark2': 90, 'mark3': 100}
{'studentId': 3, 'studentName': 'Roja', 'gender': 'Female', 'age': 15, 'mark1': 80, 'mark2': 90, 'mark3': 100}
{'studentId': 4, 'studentName': 'Nayanthara', 'gender': 'Female', 'age': 16, 'mark1': 80, 'mark2': 90, 'mark3': 100}
{'studentId': 'abdc4051', 'date': '2017-01-24', 'name': 'ab'}
{'studentId': 'abdc4052', 'date': '2017-01-22', 'name': 'abc'}

下面将把最佳答案(jsbisht)转换为接受键作为参数的函数。

const mergeArraysByKeyMatch = (array1, array2, key1, key2) => {
const map = new Map();
array1.forEach((item) => map.set(item[key1], item));
array2.forEach((item) =>
map.set(item[key2], { ...map.get(item[key2]), ...item })
);
const merged = Array.from(map.values());


return merged;
};

O (n + m)(可以归类为 O (n))解; 不带 LODASH:

// RequireAtLeastOne from https://stackoverflow.com/questions/40510611/typescript-interface-require-one-of-two-properties-to-exist/49725198#49725198
type RequireAtLeastOne<T, Keys extends keyof T = keyof T> = Pick<
T,
Exclude<keyof T, Keys>
> &
{
[K in Keys]-?: Required<Pick<T, K>> & Partial<Pick<T, Exclude<Keys, K>>>;
}[Keys];


export const mergeDualArraysOnKey = <
K extends PropertyKey,
T extends RequireAtLeastOne<{ [f in PropertyKey]?: unknown }, K>
>(
key: K,
...lists: [T[], T[]]
): T[] => {
const lookup: { [key in string]: number } = {};
return lists[0].concat(lists[1]).reduce((acc: T[], value: T, i: number) => {
const lookupKey = `${value[key]}`;
if (lookup.hasOwnProperty(lookupKey)) {
acc[lookup[lookupKey]] = Object.assign({}, acc[lookup[lookupKey]], value);
} else {
acc.push(value);
lookup[lookupKey] = acc.length - 1;
}
return acc;
}, []);
};

首先连接两个数组,然后遍历新创建的数组。它使用一个查找表(对象)来存储最终合并数组中某个项的索引,该数组具有相同的键,并将对象合并到位。

如果需要扩展以处理更多数组,可以使用循环或递归作为包装函数:

const mergeArrays = <
K extends PropertyKey,
T extends RequireAtLeastOne<{ [f in PropertyKey]?: unknown }, K>
>(
key: K,
...lists: T[][]
): T[] => {
if (lists.length === 1) {
return lists[0];
}
const l1 = lists.pop() || [];
const l2 = lists.pop() || [];
return mergeArrays(key, mergeDualArraysOnKey(key, l1, l2), ...lists);
};

用法如下:

const arr1 = [
{ id: "abdc4052", date: "2017-01-22" },
{ id: "abdc4052", location: "US" },
{ id: "abdc4051", date: "2017-01-24" },
{ id: "abdc4053", date: "2017-01-24" },
{ id: "abdc4054", date: "2017-01-24" },
{ id: "abdc4055", location: "US" },
];


const arr2 = [
{ id: "abdc4052", date: "2017-01-22" },
{ id: "abdc4052", name: "abc" },
{ id: "abdc4055", date: "2017-01-24" },
{ id: "abdc4055", date: "2017-01-24", name: "abcd" },
];


const arr3 = [{ id: "abdc4056", location: "US" }];


const arr4 = [
{ id: "abdc4056", name: "abcde" },
{ id: "abdc4051", name: "ab--ab" },
];


mergeArrays<
"id",
{
id: string;
date?: string;
location?: string;
name?: string;
}
>("id", arr1, arr2, arr3, arr4)