Fusion d'objets (tableaux associatifs)

147

Quelle est la meilleure façon / standard de fusionner deux tableaux associatifs en JavaScript? Tout le monde le fait-il simplement en roulant sa propre forboucle?

Wilhelm
la source
10
il n'y a pas de tableaux associatifs en javascript btw, seulement des objets.
gblazex
Crossref même question en Perl: stackoverflow.com/questions/350018/…
dreftymac
Tableaux associatifs en javascript: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Chris Martin

Réponses:

204

avec jquery vous pouvez appeler $.extend

var obj1 = {a: 1, b: 2};
var obj2 = {a: 4, c: 110};

var obj3 = $.extend(obj1, obj2); 

obj1 == obj3 == {a: 4, b: 2, c: 110} // Pseudo JS

(les tableaux associés sont des objets dans js)

regardez ici: http://api.jquery.com/jQuery.extend/


edit: Comme Rymo l'a suggéré, il vaut mieux le faire de cette façon:

obj3 = $.extend({}, obj1, obj2); 
obj3 == {a: 4, b: 2, c: 110}

Comme ici obj1 (et obj2) restent inchangés.


edit2: En 2018, la façon de le faire est via Object.assign:

var obj3 = Object.assign({}, obj1, obj2); 
obj3 === {a: 4, b: 2, c: 110} // Pseudo JS

Si vous travaillez avec ES6, cela peut être réalisé avec l' opérateur Spread :

const obj3 = { ...obj1, ...obj2 };
kulpae
la source
54
ou pour éviter le déblayage obj1, utilisez un objet vide comme cible:$.extend({}, obj1, obj2)
rymo
Oui, lorsque vous traitez des fermetures et des objets passés par référence, suivez les conseils de rymo pour éviter d'affecter l'objet d'origine pour les opérations suivantes.
Nathan Smith
2
Pour les navigateurs modernes, vérifiez la solution de @manfred en utilisant Object.assign(), qui ne repose pas sur JQuery si vous n'utilisez pas déjà la bibliothèque.
Phil
Cela fonctionne, mais cela modifie le premier paramètre et c'est quelque chose dont vous devez être conscient. Voir developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
kulpae
62

Maintenant, en 2016, je dirais que le meilleur moyen / standard est Object.assign ()
Pure Javascript. Aucun jQuery n'est nécessaire.

obj1 = {a: 1, b: 2};
obj2 = {a: 4, c: 110};
obj3 = Object.assign({},obj1, obj2);  // Object {a: 4, b: 2, c: 110}

Plus d'informations, d'exemples et de polyfill ici:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign

Manfred
la source
Quel est le type d'obj3? et si obj1 est de type IABC, obj3 conservera-t-il ce type par la suite?
windmaomao
49

Voici comment Prototype procède:

Object.extend = function(destination, source) {
    for (var property in source) {
        if (source.hasOwnProperty(property)) {
            destination[property] = source[property];
        }
    }
    return destination;
};

appelé comme, par exemple:

var arr1 = { robert: "bobby", john: "jack" };
var arr2 = { elizabeth: "liz", jennifer: "jen" };

var shortnames = Object.extend(arr1,arr2);

EDIT : ajout de hasOwnProperty () check comme correctement indiqué par bucabay dans les commentaires

Jonathan Fingland
la source
6
Vous aurez besoin du test source.hasOwnProperty (propriété) pour vous assurer de ne copier que les propriétés immédiates. Dans l'état
actuel des
22

Rester simple...

function mergeArray(array1,array2) {
  for(item in array1) {
    array2[item] = array1[item];
  }
  return array2;
}
Chris Scerbo
la source
6
Vous devez vérifier hasOwnProperty pour éviter de copier des propriétés sur learrau1.prototype
LeeGee
@LeeGee a raison. Vous avez besoin de ce hasOwnPropertychèque. Faire référence aux objets en tant que tableaux est également trompeur (ce sont des objets), les noms d'arguments doivent indiquer que array2 est muté ou qu'un nouvel objet doit être retourné. Je modifierais la réponse, mais en fait, elle deviendrait presque exactement la réponse modifiée de Jonathan Fingland qui a déjà été corrigée.
Vaz
14

Underscore a également une méthode d'extension:

Copiez toutes les propriétés des objets source dans l'objet de destination. C'est dans l'ordre, donc la dernière source remplacera les propriétés du même nom dans les arguments précédents.

_.extend(destination, *sources) 

_.extend({name : 'moe'}, {age : 50});
=> {name : 'moe', age : 50}
Mike McKay
la source
7

Dans dojo , la "fusion" à 2 objets / tableaux serait lang.mixin(destination, source)- vous pouvez également mélanger plusieurs sources dans une seule destination, etc. - voir la référence de la fonction mixin pour plus de détails.

Alex Martelli
la source
6

voulez-vous écraser une propriété si les noms sont identiques mais que les valeurs ne le sont pas?

Et voulez-vous changer définitivement l'un des objets d'origine,

ou voulez-vous qu'un nouvel objet fusionné soit renvoyé?

function mergedObject(obj1, obj2, force){
    for(var p in obj1) this[p]= obj1[p];
    for(var p in obj2){
        if(obj2.hasOwnProperty(p)){
            if(force || this[p]=== undefined) this[p]= obj2[p];
            else{
                n= 2;
                while(this[p+n]!== undefined)++n;
                this[p+n]= obj2[p];
            }
        }
    }
}
Kennebec
la source
6

Rouler votre Prolongez / mixin Fonction

function extend(objects) {
    var args
        , first = Array.prototype.slice.call(arguments, 0, 1)[0]
        , second;

    if (arguments.length > 1) {
        second = Array.prototype.splice.call(arguments, 1, 1)[0];
        for (var key in second) {
            first[key] = second[key];
        }
        args = Array.prototype.slice.call(arguments, 0);
        return extend.apply(this, args);
    }

    return first;
}

...

var briansDirections = {
    step1: 'Remove pastry from wrapper.',
    step2: 'Place pastry toaster.',
    step3: 'Remove pastry from toaster and enjoy.',
};
extend(briansDirections, { step1: 'Toast Poptarts' }, { step2: 'Go ahead, toast \'em' }, { step3: 'Hey, are you sill reading this???' });

...

Cela étend simplement un splat d'objets, de manière récursive. Notez également que cette fonction récursive est TCO ( Tail-Call Optimized ) car son retour est le dernier appel à lui-même.

En outre, vous souhaiterez peut-être des propriétés ciblées . Dans ce cas, vous souhaiterez peut-être condenser les objets en fonction de id,quantity ou une autre propriété. Cette approche pourrait avoir un petit livre écrit à ce sujet et nécessite une juxtaposition d'objets et peut devenir très complexe. J'ai écrit une petite bibliothèque pour cela qui est disponible sur demande.

J'espère que cela t'aides!

Cody
la source
4
  1. En Javascript il n'y a pas de notion de tableau associatif, il y a des objets
  2. La seule façon de fusionner deux objets est de faire une boucle pour leurs propriétés et de copier des pointeurs vers leurs valeurs qui ne sont pas des types primitifs et des valeurs pour des types primitifs vers une autre instance
Sergey Ilinsky
la source
8
Les objets en Javascript sont implémentés sous forme de tableaux associatifs, il y a donc certainement la notion de même
Dexygen
2

En 2019, vous avez 2 bonnes options:

Attribution d'objets [ doc ]

const result = Object.assign({}, baseObject, updatingObject);

Répartition d'objets [ doc ]

const result = { ...baseObject, ...updatingObject};

Le premier a tendance à être plus sûr, plus standard et polyvalent. Un bon pour et contre ici

VincentPerrin.com
la source
1

Yahoo UI (YUI) a également une fonction d'assistance pour cela:

http://developer.yahoo.com/yui/examples/yahoo/yahoo_merge.html

YAHOO.namespace('example');

YAHOO.example.set1 = { foo : "foo" };
YAHOO.example.set2 = { foo : "BAR", bar : "bar" };
YAHOO.example.set3 = { foo : "FOO", baz : "BAZ" };

var Ye = YAHOO.example;

var merged = YAHOO.lang.merge(Ye.set1, Ye.set2, Ye.set3);
Ben Walding
la source
1

jquery a un booléen pour la copie profonde. Vous pourriez faire quelque chose comme ça:

MergeRecursive = function(arr1, arr2){
    $.extend(true, arr1, arr2);
    return arr1;                
};

Vous pouvez également modifier cette fonction pour prendre en charge les n-tableaux à fusionner.

ArrayMergeRecursive = function(){
     if(arguments.length < 2){
          throw new Error("ArrayMergeRecursive: Please enter two or more objects to merge!");
     }

    var arr1=arguments[0];
    for(var i=0; i<=arguments.length; i++ ){
        $.extend(true, arr1, arguments[i]);                 
    }

    return arr1;                
};

Alors maintenant tu peux faire

var arr1 = {'color': {'mycolor': 'red'}, 3: 5},
    arr2 = {4: 10, 'color': {'favorite': 'green', 0: 'blue'}},
    arr3 = ['Peter','Jhon','Demosthenes'],
    results = ArrayMergeRecursive(arr1, arr2, arr3); // (arr1, arr2 ... arrN)
console.log("Result is:", results);
démosthène
la source
0

J'avais besoin d'une fusion d'objets en profondeur. Donc, toutes les autres réponses ne m'ont pas beaucoup aidé. _.extend et jQuery.extend fonctionnent bien, à moins que vous n'ayez un tableau récursif comme moi. Mais ce n'est pas si grave, vous pouvez le programmer en cinq minutes:

var deep_merge = function (arr1, arr2) {
    jQuery.each(arr2, function (index, element) {
        if (typeof arr1[index] === "object" && typeof element === "object") {
            arr1[index] = deep_merge(arr1[index], element);
        } else if (typeof arr1[index] === "array" && typeof element === "array") {
            arr1[index] = arr1[index].concat(element);
        } else {
            arr1[index] = element;
        }
    });
    return arr1;
}
Rasmus
la source
0

Pour fusionner des tableaux dans jQuery, qu'en est-il de $ .merge?

var merged = $.merge([{id:3, value:'foo3'}], [{id:1, value:'foo1'}, {id:2, value:'foo2'}]);
merged[0].id == 3;
merged[0].value == 'foo3';
merged[1].id == 1;
merged[1].value == 'foo1';
merged[2].id == 2;
merged[2].value == 'foo2';
Rogério R. Alcântara
la source
0

Solution récursive (étend également les tableaux d'objets) + null vérifié

var addProps = function (original, props) {
    if(!props) {
        return original;
    }
    if (Array.isArray(original)) {
        original.map(function (e) {
            return addProps(e, props)
        });
        return original;
    }
    if (!original) {
        original = {};
    }
    for (var property in props) {
        if (props.hasOwnProperty(property)) {
            original[property] = props[property];
        }
    }
    return original;
};

Des tests

console.log(addProps([{a: 2}, {z: 'ciao'}], {timestamp: 13}));
console.log(addProps({single: true}, {timestamp: 13}));
console.log(addProps({}, {timestamp: 13}));
console.log(addProps(null, {timestamp: 13}));

[ { a: 2, timestamp: 13 }, { z: 'ciao', timestamp: 13 } ]
{ single: true, timestamp: 13 }
{ timestamp: 13 }
{ timestamp: 13 }
sscarduzio
la source
J'ai essayé de l'utiliser avec Node JS, mais Object.getOwnPropertyNames is not a functionje planterai l'application.
Legoless
J'utilise ceci avec un moteur v8 ancien dans l'extension PostgreSQL plv8. Et cela fonctionne dans les navigateurs. Trouvez simplement un moyen dans votre moteur JS de faire la chose équivalente à "hasOwnProperty". Ensuite, vous pouvez réutiliser cette logique.
sscarduzio
0

Voici la meilleure solution.

obj1.unshift.apply( obj1, obj2 );

En outre, obj1peut se développer à l'intérieur d'une boucle sans aucun problème. (disons que obj2c'est dynamique)

bilen
la source