Объединить два массива объектов на основе ключа
У меня два массива:
Массив 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
?
Ответы
Ответ 1
Вы можете сделать это так -
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);
Ответ 2
Вы можете сделать это в одну строку
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));
Ответ 3
Вы можете использовать произвольное количество массивов и отобразить на том же индексе новые объекты.
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; }
Ответ 4
Вы можете использовать методы массива
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 = [];
function isBiggerThan10(element, index, array) {
return element > 10;
}
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"}
Ответ 5
Вы можете рекурсивно объединить их в одно:
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))
Ответ 6
Чтобы объединить два массива по id
, предполагая, что массивы равной длины:
arr1.map(item => ({
...item,
...arr2.find(({ id }) => id === item.id),
}));
Ответ 7
Ни одно из этих решений не сработало для моего случая:
- отсутствующие объекты могут существовать в любом массиве
- Сложность выполнения O (n)
примечания:
- Я использовал lodash, но его легко заменить чем-то другим
- Также используется Typescript (просто удаляйте/игнорируйте типы)
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;
}
Ответ 8
Ну... если оба массива имеют одинаковую длину, я бы, вероятно, сделал бы что-то вроде этого:
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});
}
}
Ответ 9
Использовать проект с открытым исходным кодом jinqJs очень просто -
//Use jsJinq.com open source library
var list1= [{Location: 'NY', People: 200}, {Location: 'TX', People: 500}];
var list2= [{Location: 'NY', State: 'New York'}, {Location: 'TX', State: 'Texas'}]
var result = new jinqJs().from(list1).join(list2).on('Location').select();
document.body.innerHTML += '<pre>' + JSON.stringify(result, null, 4) + '</pre>';
<script src="https://rawgit.com/fordth/jinqJs/master/jinqjs.js"></script>
Ответ 10
Я смог добиться этого с помощью вложенного отображения двух массивов и обновления исходного массива:
member.map(mem => {
return memberInfo.map(info => {
if (info.id === mem.userId) {
mem.date = info.date;
return mem;
}
}
}
Ответ 11
Здесь решение O (n), использующее Reduce и Object.assign
const joinById = ( ...lists ) =>
Object.values(
lists.reduce(
( idx, list ) => {
list.forEach( ( recod ) => {
if( idx[ recod.id ] )
idx[ recod.id ] = Object.assign( idx[ recod.id ], recod )
else
idx[ recod.id ] = recod
} )
return idx
},
{}
)
)
Каждый список сводится к одному объекту, где ключи - это идентификаторы, а значения - это объекты. Если в данном ключе уже есть значение, для него вызывается object.assign и текущая запись.
Здесь общее решение 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 )
По сути, это то же самое, что и метод joinById, за исключением того, что он сохраняет индексный объект для идентификации записей, которые нужно объединить. Записи хранятся в массиве, а индекс хранит позицию записи для данного набора ключей и количество списков, в которых он был найден.
Каждый раз, когда встречается один и тот же набор ключей, узел обнаруживается в дереве, элемент с индексом обновляется, и число раз, когда он был найден, увеличивается.
наконец, объект idx удаляется из массива со срезом, все элементы, которые не были найдены в каждом наборе, удаляются. Это делает его внутренним соединением, вы можете удалить этот фильтр и получить full outer join.
наконец, каждый элемент сопоставляется с его значением, и у вас есть объединенный массив.
Ответ 12
Если у вас есть 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);