Como concatenar propriedades de vários objetos JavaScript

105

Estou procurando a melhor maneira de "adicionar" vários objetos JavaScript (matrizes associativas).

Por exemplo, dado:

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

qual é a melhor maneira de calcular:

{ "one" : 1, "two" : 2, "three" : 3, "four" : 4, "five" : 5 }
Vlad
fonte
3
Uma nota semântica: apesar da sintaxe [], eles não são arrays. A ordem não é realmente garantida.
Álvaro González
1
A ordem de iteração não é garantida pelos padrões da ecma. Mas é a forma como é implementado na maioria dos navegadores. (De John Resig) Este comportamento é explicitamente deixado indefinido pela especificação ECMAScript. Na ECMA-262, seção 12.6.4: A mecânica de enumerar as propriedades ... depende da implementação. No entanto, a especificação é bastante diferente da implementação. Todas as implementações modernas do ECMAScript iteram por meio das propriedades do objeto na ordem em que foram definidas. Por causa disso, a equipe do Chrome considerou isso um bug e irá corrigi-lo.
Juan Mendes,

Respostas:

159

ECMAscript 6 introduzido Object.assign()para conseguir isso nativamente em Javascript.

O método Object.assign () é usado para copiar os valores de todas as propriedades próprias enumeráveis ​​de um ou mais objetos de origem para um objeto de destino. Ele retornará o objeto de destino.

Documentação MDN em 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.assigné suportado em muitos navegadores modernos, mas ainda não em todos. Use um transpiler como Babel e Traceur para gerar JavaScript ES5 compatível com versões anteriores.

filoxo
fonte
4
Este é um dos melhores que eu poderia dizer. Como E6 agora é mais usado, Object.assign é a melhor resposta.
Vimalraj Selvam
6
Se você não sabe quantos objetos precisam ser mesclados, porque eles estão em uma matriz, você pode mesclá-los da seguinte forma:Object.assign.apply({}, [{a: 1}, {b: 2}, ....])
Jeanluca Scaljeri
1
Uma alternativa ao uso Object.assign.applypara mesclar uma matriz de objetos é usar o operador de propagação:Object.assign( ...objects )
Spen
@Spen que já está incluído como resposta a esta pergunta. Não vi benefício em duplicá-lo aqui.
filoxo
após 4 horas, esta resposta resolveu meu problema no Angular 7. Obrigado pela simplicidade e precisão da resposta.
Gel de
35

Você pode usar o jquery $.extendassim:

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
fonte
1 Mesmo se você não usar o método jQuery, você pode usar o código deles para ajudar a construir sua própria (talvez, mais específica) implementação.
tvanfosson
25
@Randal: Existem muitas razões perfeitamente boas para não usar o jQuery.
Tim Down,
3
Editar:d = $.extend({},a,b,c);
Bob Stein
34

Isso deve servir:

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

Resultado:

{
  "one": 1,
  "two": 2,
  "three": 3,
  "four": 4,
  "five": 5
}
cletus
fonte
Não lengthpesquisa o tamanho da matriz em cada invocação? Estou tão acostumada a escrever for (var i = 0, len = array.length; i < len; ++i)que não consigo lembrar de cara por que comecei a fazer isso.
tvanfosson
1
Sim esta correto. É melhor desempenho armazenar em cache o comprimento uma vez. No entanto, como o tamanho dos argumentos "array" provavelmente não será muito grande, isso realmente não importará neste caso.
jhurshman
1
Não, exceto ligeiramente no IE6. Acessar a propriedade length custa o mesmo que acessar a variável len.
Alsciende
1
@ Juan Eu acredito que você está errado, e fiz alguns testes para me decidir. Armazenar o tamanho do cache é um mito fácil de otimização que está obsoleto há muitos anos e torna o código (ligeiramente) menos legível. Na verdade, o armazenamento em cache às vezes torna o navegador mais lento (Safari).
Alsciende
2
Não seria melhor escrever "para (var p em ..." em vez de "para (p em ..."?
Hugo
24

ECMAScript 6 espalhou sintaxe . E agora você pode fazer isso:

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

O sublinhado tem poucos métodos para fazer isso;

1. _.extend (destino, * fontes)

Copie todas as propriedades dos objetos de origem para o objeto de destino e retorne o objeto de destino .

_.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 (objeto, * padrões)

Preencha as propriedades indefinidas no objeto com valores dos objetos padrão e retorne o objeto .

_.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
fonte
4

Por que a função deve ser restrita a 3 argumentos? Além disso, verifique 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
fonte
4

A clonagem superficial (excluindo protótipo) ou mesclagem de objetos agora é possível usando uma sintaxe mais curta do que Object.assign () .

A sintaxe de propagação para literais de objeto foi introduzida no 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 }

O operador Spread (...) é suportado em muitos navegadores modernos, mas não em todos.

Portanto, é recomendável usar um transpiler como o Babel para converter o código ECMAScript 2015+ em uma versão compatível com versões anteriores de JavaScript em navegadores ou ambientes atuais e mais antigos.

Este é o código equivalente que o Babel irá gerar para você:

"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
fonte
Esta é uma resposta muito mais robusta do que a resposta aceita. Obrigado!
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: as propriedades existentes nos objetos anteriores serão sobrescritas.

Björn
fonte
2
Isso tem o efeito colateral que a === dno final. Isso pode estar ok ou não.
tvanfosson
2

É fácil usar o operador spread ES7 para um objeto, no console do seu navegador, coloque

({ name: "Alex", ...(true  ? { age: 19 } : { })}) // {name: "Alex", age: 19}
({ name: "Alex", ...(false ? { age: 19 } : { })}) // {name: "Alex",        }
Purkhalo Alex
fonte
1
Agradável. Esta pergunta tem mais de 10 anos, feliz que uma operação tão simples seja fácil agora. Não era tão fácil antes, se você olhar para as respostas das outras pessoas.
Vlad
é por isso que deixei minha própria resposta :)
Purkhalo Alex
2

Para mesclar um número dinâmico de objetos, podemos usar Object.assigncom sintaxe de propagação .

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

A função acima aceita qualquer número de objetos, mesclando todas as suas propriedades em um novo objeto com propriedades de objetos posteriores sobrescrevendo aquelas de objetos anteriores.

Demo:

Para mesclar uma matriz de objetos, um método semelhante pode ser aplicado.

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

Demo:

hev1
fonte
1

Provavelmente, a maneira mais rápida, eficiente e genérica é esta (você pode mesclar qualquer número de objetos e até mesmo copiar para o primeiro -> atribuir):

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

Ele também permite que você modifique o primeiro objeto conforme ele passa por referência. Se você não deseja isso, mas deseja um objeto completamente novo contendo todas as propriedades, você pode passar {} como o primeiro 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); 

combinado_object e object1 contêm as propriedades 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); 

Nesse caso, o combinado_objeto contém as propriedades de objeto1, objeto2, objeto3, mas objeto1 não é modificado.

Verifique aqui: https://jsfiddle.net/ppwovxey/1/

Nota: os objetos JavaScript são passados ​​por referência.

Selay
fonte
1

ES6 ++

A questão é adicionar vários objetos diferentes em um.

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

digamos que você tenha um objeto com várias chaves que são objetos:

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

esta foi a solução que encontrei (ainda tenho que foreach: /)

let objAll = {};

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

Fazendo isso, podemos adicionar dinamicamente TODAS as chaves de objeto em uma.

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

Mais simples: espalhar operadores

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