JavaScript fusionando objetos por identificación
¿Cuál es la forma correcta de fusionar dos matrices en Javascript?
Tengo dos matrices (por ejemplo):
var a1 = [{ id : 1, name : "test"}, { id : 2, name : "test2"}]
var a2 = [{ id : 1, count : "1"}, {id : 2, count : "2"}]
Quiero poder terminar con algo como:
var a3 = [{ id : 1, name : "test", count : "1"},
{ id : 2, name : "test2", count : "2"}]
Donde las dos matrices se unen según el campo 'id' y simplemente se agregan datos adicionales.
Intenté usar _.union
para hacer esto, pero simplemente sobrescribe los valores de la segunda matriz en la primera.
Solución corta ES6
const a3 = a1.map(t1 => ({...t1, ...a2.find(t2 => t2.id === t1.id)}))
Esto debería funcionar:
var mergedList = _.map(a1, function(item){
return _.extend(item, _.findWhere(a2, { id: item.id }));
});
Esto supone que la identificación del segundo objeto en a1 debería ser 2 en lugar de "2".
Suponiendo que los ID son cadenas y el orden no importa, puede
- Crea una tabla hash.
- Itere ambas matrices y almacene los datos en la tabla hash, indexados por el ID. Si ya hay algunos datos con ese ID, actualícelos con
Object.assign
(ES6, se puede rellenar ). - Obtenga una matriz con los valores del mapa hash.
var hash = Object.create(null);
a1.concat(a2).forEach(function(obj) {
hash[obj.id] = Object.assign(hash[obj.id] || {}, obj);
});
var a3 = Object.keys(hash).map(function(key) {
return hash[key];
});
En ECMAScript6, si los ID no son necesariamente cadenas, puede usar Map
:
var hash = new Map();
a1.concat(a2).forEach(function(obj) {
hash.set(obj.id, Object.assign(hash.get(obj.id) || {}, obj))
});
var a3 = Array.from(hash.values());
ES6 simplifica esto:
let merge = (obj1, obj2) => ({...obj1, ...obj2});
Tenga en cuenta que las claves repetidas se fusionarán, prevalecerá el valor del segundo objeto y se ignorará el valor repetido del primer objeto .
Ejemplo:
let obj1 = {id: 1, uniqueObj1Key: "uniqueKeyValueObj1", repeatedKey: "obj1Val"};
let obj2 = {id: 1, uniqueObj2Key: "uniqueKeyValueObj2", repeatedKey: "obj2Val"};
merge(obj1, obj2)
// {id: 1, uniqueObj1Key: "uniqueKeyValueObj1", repeatedKey: "obj2Val", uniqueObj2Key: "uniqueKeyValueObj2"}
merge(obj2, obj1)
// {id: 1, uniqueObj2Key: "uniqueKeyValueObj2", repeatedKey: "obj1Val", uniqueObj1Key: "uniqueKeyValueObj1"}
Solución completa (con Lodash , no Underscore )
var a1 = [{ id : 1, name : "test"}, { id : 2, name : "test2"}]
var a2 = [{ id : 1, count : "1"}, {id : 2, count : "2"}]
var merge = (obj1, obj2) => ({...obj1, ...obj2});
_.zipWith(a1, a2, merge)
(2) [{…}, {…}]
0: {id: 1, name: "test", count: "1"}
1: {id: 2, name: "test2", count: "2"}
Si tiene una serie de matrices para fusionar, puede hacerlo así:
var arrayOfArraysToMerge = [a1, a2, a3, a4]; //a3 and a4 are arrays like a1 and a2 but with different properties and same IDs.
_.zipWith(...arrayOfArraysToMerge, merge)
(2) [{…}, {…}]
0: {id: 1, name: "test", count: "1", extra1: "val1", extra2: 1}
1: {id: 2, name: "test2", count: "2", extra1: "val2", extra2: 2}