Quelle est la bonne façon de fusionner deux tableaux en Javascript?
J'ai deux tableaux (par exemple):
var a1 = [{ id : 1, name : "test"}, { id : 2, name : "test2"}]
var a2 = [{ id : 1, count : "1"}, {id : 2, count : "2"}]
Je veux pouvoir me retrouver avec quelque chose comme:
var a3 = [{ id : 1, name : "test", count : "1"},
{ id : 2, name : "test2", count : "2"}]
Lorsque les deux tableaux sont joints en fonction du champ 'id' et que des données supplémentaires sont simplement ajoutées.
J'ai essayé d'utiliser _.union
pour le faire, mais cela écrase simplement les valeurs du second tableau dans le premier.
Cela devrait faire l'affaire:
var mergedList = _.map(a1, function(item){
return _.extend(item, _.findWhere(a2, { id: item.id }));
});
Cela suppose que l'id du second objet dans a1 devrait être 2 plutôt que "2"
En supposant que les identifiants sont des chaînes et que l'ordre n'a pas d'importance, vous pouvez
Object.assign
(ES6, peut être polyfilled ).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];
});
Dans ECMAScript6, si les ID ne sont pas nécessairement des chaînes, vous pouvez utiliser 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());
réduire version.
var a3 = a1.concat(a2).reduce((acc, x) => {
acc[x.id] = Object.assign(acc[x.id] || {}, x);
return acc;
}, {});
_.values(a3);
Je pense que c'est une pratique courante dans le langage fonctionnel.
La mise en oeuvre de lodash:
var merged = _.map(a1, function(item) {
return _.assign(item, _.find(a2, ['id', item.id]));
});
Le résultat:
[
{
"id":1,
"name":"test",
"count":"1"
},
{
"id":2,
"name":"test2",
"count":"2"
}
]
ES6 simplifie ceci:
let merge = (obj1, obj2) => ({...obj1, ...obj2});
Notez que les clés répétées seront fusionnées et que la valeur du second objet prévaudra et la valeur répétée du premier objet sera ignorée .
Exemple:
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"}
Il y a déjà beaucoup de bonnes réponses, je vais simplement en ajouter une autre, qui est un vrai problème que j'avais besoin de résoudre hier.
J'avais un tableau de messages avec des identifiants d'utilisateur et un tableau d'utilisateurs contenant les noms des utilisateurs et d'autres détails. C’est ainsi que j’ai réussi à ajouter des détails d’utilisateur aux messages.
var messages = [{userId: 2, content: "Salam"}, {userId: 5, content: "Hello"},{userId: 4, content: "Moi"}];
var users = [{id: 2, name: "Grace"}, {id: 4, name: "Janetta"},{id: 5, name: "Sara"}];
var messagesWithUserNames = messages.map((msg)=> {
var haveEqualId = (user) => user.id === msg.userId
var userWithEqualId= users.find(haveEqualId)
return Object.assign({}, msg, userWithEqualId)
})
console.log(messagesWithUserNames)
const a3 = a1.map(it1 => {
it1.test = a2.find(it2 => it2.id === it1.id).test
return it1
})
Cela devrait faire l'affaire en es6 et c'est fonctionnel;
const a1 = [{ id: 1, name: 'test' }, { id: 2, name: 'test2' }];
const a2 = [{ id: 1, count: '1' }, { id: 2, count: '2' }];
const mergeArray = (source, merge, by) => source.map(item => ({
...item,
...(merge.find(i => i[by] === item[by]) || {}),
}));
console.log(mergeArray(a1, a2, 'id'));
// > [{ id: 1, name: 'test', count: '1' }, { id: 2, name: 'test2', count: '2' }]
Cela suppose également que l'id du deuxième objet de a1 devrait être 2 plutôt que "2".
Une version de travail TypeScript:
export default class Merge {
static byKey(a1: any[], a2: any[], key: string) {
const res = a1.concat(a2).reduce((acc, x) => {
acc[x[key]] = Object.assign(acc[x[key]] || {}, x);
return acc;
}, {});
return Object.entries(res).map(pair => {
const [, value] = pair;
return value;
});
}
}
test("Merge", async () => {
const a1 = [{ id: "1", value: "1" }, { id: "2", value: "2" }];
const a2 = [{ id: "2", value: "3" }];
expect(Merge.byKey(a1, a2, "id")).toStrictEqual([
{
id: "1",
value: "1"
},
{ id: "2", value: "3" }
]);
});
Vous pouvez écrire une fonction de fusion d'objet simple comme celle-ci
function mergeObject(cake, icing) {
var icedCake = {}, ingredient;
for (ingredient in cake)
icedCake[ingredient] = cake[ingredient];
for (ingredient in icing)
icedCake[ingredient] = icing[ingredient];
return icedCake;
}
Ensuite, vous devez utiliser une double boucle pour l’appliquer à votre structure de données.
var i, j, a3 = a1.slice();
for (i = 0; i < a2.length; ++i) // for each item in a2
for (j = 0; i < a3.length; ++i) // look at items in other array
if (a2[i]['id'] === a3[j]['id']) // if matching id
a3[j] = mergeObject(a3[j], a2[i]); // merge
Vous pouvez également utiliser mergeObject
comme clone simple en transmettant un paramètre sous forme d'objet vide.