Cómo concatenar propiedades de varios objetos JavaScript

105

Estoy buscando la mejor manera de "agregar" múltiples objetos JavaScript (matrices asociativas).

Por ejemplo, dado:

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

cuál es la mejor manera de calcular:

{ "one" : 1, "two" : 2, "three" : 3, "four" : 4, "five" : 5 }
Vlad
fuente
3
Una nota semántica: a pesar de la sintaxis [], no son matrices en absoluto. El pedido no está realmente garantizado.
Álvaro González
1
El orden de iteración no está garantizado según los estándares de la ecma. Pero es la forma en que se implementa en la mayoría de los navegadores. (De John Resig) Este comportamiento se deja explícitamente sin definir por la especificación ECMAScript. En ECMA-262, sección 12.6.4: La mecánica de enumerar las propiedades ... depende de la implementación. Sin embargo, la especificación es bastante diferente a la implementación. Todas las implementaciones modernas de ECMAScript iteran a través de las propiedades del objeto en el orden en que fueron definidas. Debido a esto, el equipo de Chrome ha considerado que se trata de un error y lo solucionará.
Juan Mendes

Respuestas:

159

Se introdujo ECMAscript 6 Object.assign()para lograr esto de forma nativa en Javascript.

El método Object.assign () se utiliza para copiar los valores de todas las propiedades propias enumerables de uno o más objetos de origen a un objeto de destino. Devolverá el objeto de destino.

Documentación de MDN sobre 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.assignes compatible con muchos navegadores modernos, pero todavía no todos. Utilice un transpilador como Babel y Traceur para generar JavaScript ES5 compatible con versiones anteriores.

filoxo
fuente
4
Este es uno de los mejores que puedo decir. Dado que ahora se usa principalmente E6, Object.assign es la mejor respuesta.
Vimalraj Selvam
6
Si no sabe cuántos objetos deben fusionarse, ya que están en una matriz, puede fusionarlos de la siguiente manera:Object.assign.apply({}, [{a: 1}, {b: 2}, ....])
Jeanluca Scaljeri
1
Una alternativa a usar Object.assign.applypara fusionar una matriz de objetos es usar el operador de propagación:Object.assign( ...objects )
Spen
@Spen que ya está incluido como respuesta a esta pregunta. No veo ningún beneficio en duplicarlo aquí.
filoxo
después de 4 horas, esta respuesta resolvió mi problema en Angular 7. Gracias por la simplicidad y precisión de la respuesta.
Gel
35

Podrías usar jquery $.extendcomo este:

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
fuente
+1 Incluso si no usa el método de jQuery, podría usar su código para ayudar a construir su propia implementación (quizás más específica).
tvanfosson
25
@Randal: Hay muchas razones perfectamente buenas para no usar jQuery.
Tim Down
3
Edición:d = $.extend({},a,b,c);
Bob Stein
34

Esto debería hacerlo:

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);

Salida:

{
  "one": 1,
  "two": 2,
  "three": 3,
  "four": 4,
  "five": 5
}
cletus
fuente
¿No lengthbusca el tamaño de la matriz en cada invocación? Estoy tan acostumbrado a escribir for (var i = 0, len = array.length; i < len; ++i)que no puedo recordar por qué empecé a hacerlo.
tvanfosson
1
Si eso es correcto. Es mejor almacenar en caché la longitud una vez. Sin embargo, debido a que el tamaño de la "matriz" de argumentos probablemente nunca será muy grande, en este caso realmente no importará.
jhurshman
1
No, excepto ligeramente en IE6. Acceder a la propiedad length cuesta lo mismo que acceder a la variable len.
Alsciende
1
@Juan Creo que estás equivocado, e hice algunas pruebas para tomar una decisión. El almacenamiento en caché de la longitud es un mito fácil de optimización que ha estado obsoleto durante muchos años y hace que el código (ligeramente) sea menos legible. En realidad, almacenar en caché la longitud a veces ralentiza el navegador (Safari).
Alsciende
2
¿No sería mejor escribir 'for (var p en ...' en lugar de 'para (p en ...'?
Hugo
24

ECMAScript 6 ha extendido la sintaxis . Y ahora puedes hacer esto:

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
fuente
12

Underscore tiene pocos métodos para hacer esto;

1. _.extend (destino, * fuentes)

Copie todas las propiedades de los objetos de origen en el objeto de destino y devuelva el objeto de destino .

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

O

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

2. _.defaults (objeto, * valores predeterminados)

Complete las propiedades no definidas en el objeto con valores de los objetos predeterminados y devuelva el objeto .

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

O

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

¿Por qué la función debería restringirse a 3 argumentos? Además, busque 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
fuente
4

La clonación superficial (excluyendo el prototipo) o la fusión de objetos ahora es posible usando una sintaxis más corta que Object.assign () .

La sintaxis de propagación para literales de objeto se introdujo en 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 }

El operador Spread (...) se admite en muchos navegadores modernos, pero no en todos.

Por lo tanto, se recomienda utilizar un transpilador como Babel para convertir el código ECMAScript 2015+ en una versión retrocompatible de JavaScript en navegadores o entornos actuales y antiguos.

Este es el código equivalente que Babel generará para usted:

"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
fuente
Esta es una respuesta mucho más sólida que la respuesta aceptada. ¡Gracias!
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;
}

Aviso: se sobrescribirán las propiedades existentes en los objetos anteriores.

Björn
fuente
2
Esto tiene el efecto secundario que a === dal final. Eso podría estar bien y puede que no.
tvanfosson
2

Es fácil usar el operador de propagación ES7 para un objeto, en la consola de su navegador

({ name: "Alex", ...(true  ? { age: 19 } : { })}) // {name: "Alex", age: 19}
({ name: "Alex", ...(false ? { age: 19 } : { })}) // {name: "Alex",        }
Purkhalo Alex
fuente
1
Agradable. Esta pregunta tiene más de 10 años, me alegro de que una operación tan simple sea fácil ahora. Antes no era tan fácil si miras las respuestas de otras personas.
Vlad
por eso dejé mi propia respuesta :)
Purkhalo Alex
2

Para fusionar un número dinámico de objetos, podemos usar Object.assigncon sintaxis extendida .

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

La función anterior acepta cualquier número de objetos, fusionando todas sus propiedades en un nuevo objeto con propiedades de objetos posteriores que sobrescriben las de los objetos anteriores.

Manifestación:

Para fusionar una matriz de objetos, se puede aplicar un método similar.

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

Manifestación:

hev1
fuente
1

Probablemente, la forma más rápida, eficiente y genérica es esta (puede fusionar cualquier número de objetos e incluso copiar al primero -> asignar):

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];
}

También le permite modificar el primer objeto tal como pasó por referencia. Si no quiere esto pero quiere tener un objeto completamente nuevo que contenga todas las propiedades, puede pasar {} como primer argumento.

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); 

objeto_combinado y objeto1 contienen las propiedades de objeto1, objeto2, objeto3.

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); 

En este caso, el objeto_combinado contiene las propiedades de object1, object2, object3 pero object1 no se modifica.

Consulte aquí: https://jsfiddle.net/ppwovxey/1/

Nota: los objetos JavaScript se pasan por referencia.

Selay
fuente
1

ES6 ++

La pregunta es agregar varios objetos diferentes en uno.

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

digamos que tiene un objeto con varias claves que son objetos:

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

esta fue la solución que encontré (todavía tengo que foreach: /)

let objAll = {};

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

Al hacer esto, podemos agregar dinámicamente TODAS las claves de objeto en una.

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

El más simple: operadores de propagación

var obj1 = {a: 1}
var obj2 = {b: 2}
var concat = { ...obj1, ...obj2 } // { a: 1, b: 2 }
Rodolfo Paranhos
fuente
-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
fuente