Comment concaténer les propriétés de plusieurs objets JavaScript

105

Je recherche la meilleure façon d '"ajouter" plusieurs objets JavaScript (tableaux associatifs).

Par exemple, étant donné:

a = { "one" : 1, "two" : 2 };
b = { "three" : 3 };
c = { "four" : 4, "five" : 5 };

quelle est la meilleure façon de calculer:

{ "one" : 1, "two" : 2, "three" : 3, "four" : 4, "five" : 5 }
Vlad
la source
3
Une note sémantique: malgré la syntaxe [], ce ne sont pas du tout des tableaux. L'ordre n'est pas vraiment garanti.
Álvaro González
1
L'ordre d'itération n'est pas garanti par les normes ecma. Mais c'est ainsi qu'il est implémenté dans la plupart des navigateurs. (De John Resig) Ce comportement est explicitement laissé indéfini par la spécification ECMAScript. Dans ECMA-262, section 12.6.4: La mécanique de l'énumération des propriétés ... dépend de l'implémentation. Cependant, la spécification est assez différente de l'implémentation. Toutes les implémentations modernes d'ECMAScript parcourent les propriétés d'objet dans l'ordre dans lequel elles ont été définies. Pour cette raison, l'équipe Chrome a considéré qu'il s'agissait d'un bogue et le corrigera.
Juan Mendes

Réponses:

159

ECMAscript 6 introduit Object.assign()pour y parvenir nativement en Javascript.

La méthode Object.assign () est utilisée pour copier les valeurs de toutes les propriétés propres énumérables d'un ou plusieurs objets source vers un objet cible. Il renverra l'objet cible.

Documentation MDN sur Object.assign ()

var o1 = { a: 1 };
var o2 = { b: 2 };
var o3 = { c: 3 };

var obj = Object.assign({}, o1, o2, o3);
console.log(obj); // { a: 1, b: 2, c: 3 }

Object.assignest pris en charge dans de nombreux navigateurs modernes mais pas encore tous. Utilisez un transpilateur comme Babel et Traceur pour générer du JavaScript ES5 rétrocompatible.

filoxo
la source
4
C'est l'un des meilleurs que je puisse dire. Puisque E6 est maintenant principalement utilisé, Object.assign est la meilleure réponse.
Vimalraj Selvam
6
Si vous ne savez pas combien d'objets doivent être fusionnés, parce qu'ils sont dans un tableau, vous pouvez les fusionner comme suit:Object.assign.apply({}, [{a: 1}, {b: 2}, ....])
Jeanluca Scaljeri
1
Une alternative à l'utilisation Object.assign.applypour fusionner un tableau d'objets, consiste à utiliser à la place l'opérateur de propagation:Object.assign( ...objects )
Spen
@Spen qui est déjà inclus comme réponse à cette question. Je ne vois aucun avantage à le reproduire ici.
filoxo
après 4 heures, cette réponse a résolu mon problème dans Angular 7. Merci pour la simplicité et l'exactitude de la réponse.
Gel du
35

Vous pouvez utiliser jquery $.extendcomme ceci:

let a = { "one" : 1, "two" : 2 },
    b = { "three" : 3 },
    c = { "four" : 4, "five" : 5 };

let d = $.extend({}, a, b, c)

console.log(d)
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

adiga
la source
+1 Même si vous n'utilisez pas la méthode de jQuery, vous pouvez utiliser leur code pour vous aider à construire votre propre implémentation (peut-être plus spécifique).
tvanfosson
25
@Randal: Il existe de nombreuses raisons parfaitement bonnes pour ne pas utiliser jQuery.
Tim Down
3
Edit:d = $.extend({},a,b,c);
Bob Stein
34

Cela devrait le faire:

function collect() {
  var ret = {};
  var len = arguments.length;
  for (var i = 0; i < len; i++) {
    for (p in arguments[i]) {
      if (arguments[i].hasOwnProperty(p)) {
        ret[p] = arguments[i][p];
      }
    }
  }
  return ret;
}

let a = { "one" : 1, "two" : 2 };
let b = { "three" : 3 };
let c = { "four" : 4, "five" : 5 };

let d = collect(a, b, c);
console.log(d);

Production:

{
  "one": 1,
  "two": 2,
  "three": 3,
  "four": 4,
  "five": 5
}
cletus
la source
Ne lengthrecherche pas la taille du tableau à chaque appel? J'ai tellement l'habitude d'écrire for (var i = 0, len = array.length; i < len; ++i)que je ne me souviens pas du tout pourquoi j'ai commencé à le faire.
tvanfosson
1
Oui c'est correct. Il est préférable de mettre en cache la longueur une fois. Cependant, comme la taille des arguments "array" ne sera probablement jamais très grande, cela n'aura pas vraiment d'importance dans ce cas.
jhurshman
1
Non, sauf légèrement sur IE6. L'accès à la propriété length coûte le même prix que l'accès à la variable len.
Alsciende
1
@Juan Je pense que vous vous trompez, et j'ai fait quelques tests pour me décider. La mise en cache de la longueur est un mythe facile d'optimisation qui est obsolète depuis de nombreuses années et qui rend le code (légèrement) moins lisible. En fait, la mise en cache de la longueur ralentit parfois le navigateur (Safari).
Alsciende
2
Ne serait-il pas préférable d'écrire 'for (var p in ...' au lieu de 'for (p in ...'?
Hugo
24

ECMAScript 6 a une syntaxe étendue . Et maintenant vous pouvez faire ceci:

const obj1 = { 1: 11, 2: 22 };
const obj2 = { 3: 33, 4: 44 };
const obj3 = { ...obj1, ...obj2 };

console.log(obj3); // {1: 11, 2: 22, 3: 33, 4: 44}

Valex
la source
12

Underscore a peu de méthodes pour faire cela;

1. _.extend (destination, * sources)

Copiez toutes les propriétés des objets source dans l' objet de destination et renvoyez l' objet de destination .

_.extend(a, _.extend(b, c));
=> {"one" : 1, "two" : 2, "three" : 3, "four" : 4, "five" : 5 }

Ou

_.extend(a, b);
=> {"one" : 1, "two" : 2, "three" : 3}
_.extend(a, c);
=> {"one" : 1, "two" : 2, "three" : 3, "four" : 4, "five" : 5 }

2. _.defaults (objet, * par défaut)

Remplissez les propriétés non définies dans object avec les valeurs des objets par défaut et renvoyez l' objet .

_.defaults(a, _.defaults(b, c));
=> {"one" : 1, "two" : 2, "three" : 3, "four" : 4, "five" : 5 }

Ou

_.defaults(a, b);
=> {"one" : 1, "two" : 2, "three" : 3}
_.defaults(a, c);
=> {"one" : 1, "two" : 2, "three" : 3, "four" : 4, "five" : 5 }
gihanchanuka
la source
4

Pourquoi la fonction devrait-elle être limitée à 3 arguments? Vérifiez également hasOwnProperty.

function Collect() {
    var o={};
    for(var i=0;i<arguments.length;i++) {
      var arg=arguments[i];
      if(typeof arg != "object") continue;
      for(var p in arg) {
        if(arg.hasOwnProperty(p)) o[p] = arg[p];
      }
    }
    return o;
}
Alsciende
la source
4

Le clonage superficiel (hors prototype) ou la fusion d'objets est désormais possible en utilisant une syntaxe plus courte que Object.assign () .

La syntaxe de propagation pour les littéraux d'objet a été introduite dans ECMAScript 2018 ):

const a = { "one": 1, "two": 2 };
const b = { "three": 3 };
const c = { "four": 4, "five": 5 };

const result = {...a, ...b, ...c};
// Object { "one": 1, "two": 2 , "three": 3, "four": 4, "five": 5 }

L'opérateur Spread (...) est pris en charge dans de nombreux navigateurs modernes, mais pas tous.

Il est donc recommandé d'utiliser un transpilateur comme Babel pour convertir le code ECMAScript 2015+ en une version rétrocompatible de JavaScript dans les navigateurs ou environnements actuels et anciens.

Voici le code équivalent que Babel générera pour vous:

"use strict";

var _extends = Object.assign || function(target) {
  for (var i = 1; i < arguments.length; i++) {
    var source = arguments[i];
    for (var key in source) {
      if (Object.prototype.hasOwnProperty.call(source, key)) {
        target[key] = source[key];
      }
    }
  }
  return target;
};

var a = { "one": 1, "two": 2 };
var b = { "three": 3 };
var c = { "four": 4, "five": 5 };

var result = _extends({}, a, b, c);
// Object { "one": 1, "two": 2 , "three": 3, "four": 4, "five": 5 }
Thiago Santos
la source
C'est une réponse beaucoup plus robuste que la réponse acceptée. Merci!
Kaleb Anderson
3
function Collect(a, b, c) {
    for (property in b)
        a[property] = b[property];

    for (property in c)
        a[property] = c[property];

    return a;
}

Remarque: les propriétés existantes dans les objets précédents seront écrasées.

Björn
la source
2
Cela a pour effet secondaire qu'à a === dla fin. Cela pourrait être correct et non.
tvanfosson
2

Il est facile d'utiliser l' opérateur de propagation ES7 pour un objet, dans la console de votre navigateur

({ name: "Alex", ...(true  ? { age: 19 } : { })}) // {name: "Alex", age: 19}
({ name: "Alex", ...(false ? { age: 19 } : { })}) // {name: "Alex",        }
Purkhalo Alex
la source
1
Agréable. Cette question a plus de 10 ans, heureux qu'une opération aussi simple soit facile maintenant. Ce n'était pas si facile avant si vous regardez les réponses des autres.
Vlad
c'est pourquoi j'ai laissé ma propre réponse :)
Purkhalo Alex
2

Pour fusionner un nombre dynamique d'objets, nous pouvons utiliser Object.assignavec la syntaxe de propagation .

const mergeObjs = (...objs) => Object.assign({}, ...objs);

La fonction ci-dessus accepte n'importe quel nombre d'objets, fusionnant toutes leurs propriétés dans un nouvel objet avec les propriétés des objets ultérieurs remplaçant celles des objets précédents.

Démo:

Pour fusionner un tableau d'objets, une méthode similaire peut être appliquée.

const mergeArrayOfObjs = arr => Object.assign({}, ...arr);

Démo:

hev1
la source
1

Probablement, le moyen le plus rapide, efficace et plus générique est celui-ci (vous pouvez fusionner n'importe quel nombre d'objets et même copier dans le premier -> attribuer):

function object_merge(){
    for (var i=1; i<arguments.length; i++)
       for (var a in arguments[i])
         arguments[0][a] = arguments[i][a];
   return arguments[0];
}

Il vous permet également de modifier le premier objet tel qu'il est passé par référence. Si vous ne le souhaitez pas mais souhaitez avoir un objet complètement nouveau contenant toutes les propriétés, vous pouvez passer {} comme premier argument.

var object1={a:1,b:2};
var object2={c:3,d:4};
var object3={d:5,e:6};
var combined_object=object_merge(object1,object2,object3); 

combiné_object et object1 contiennent tous deux les propriétés de object1, object2, object3.

var object1={a:1,b:2};
var object2={c:3,d:4};
var object3={d:5,e:6};
var combined_object=object_merge({},object1,object2,object3); 

Dans ce cas, le combine_object contient les propriétés de object1, object2, object3 mais object1 n'est pas modifié.

Vérifiez ici: https://jsfiddle.net/ppwovxey/1/

Remarque: les objets JavaScript sont passés par référence.

Selay
la source
1

ES6 ++

La question est d'ajouter différents objets en un seul.

let obj = {};
const obj1 = { foo: 'bar' };
const obj2 = { bar: 'foo' };
Object.assign(obj, obj1, obj2);
//output => {foo: 'bar', bar: 'foo'};

disons que vous avez un objet avec plusieurs clés qui sont des objets:

let obj = {
  foo: { bar: 'foo' },
  bar: { foo: 'bar' }
}

c'était la solution que j'ai trouvée (je dois encore foreach: /)

let objAll = {};

Object.values(obj).forEach(o => {
  objAll = {...objAll, ...o};
});

En faisant cela, nous pouvons ajouter dynamiquement TOUTES les clés d'objet en une seule.

// Output => { bar: 'foo', foo: 'bar' }
Joseph Briggs
la source
0

Le plus simple: les opérateurs de diffusion

var obj1 = {a: 1}
var obj2 = {b: 2}
var concat = { ...obj1, ...obj2 } // { a: 1, b: 2 }
Rodolfo Paranhos
la source
-2
function collect(a, b, c){
    var d = {};

    for(p in a){
        d[p] = a[p];
    }
    for(p in b){
        d[p] = b[p];
    }
    for(p in c){
        d[p] = c[p];
    }

    return d;
}
Álvaro González
la source