Mesclando objetos (matrizes associativas)

Respostas:

204

com jquery você pode chamar $.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

(matrizes associadas são objetos em js)

veja aqui: http://api.jquery.com/jQuery.extend/


edit: Como rymo sugeriu, é melhor fazê-lo desta maneira:

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

Como aqui obj1 (e obj2) permanecem inalterados.


edit2: Em 2018, a maneira de fazer isso é via Object.assign:

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

Se estiver trabalhando com o ES6, isso pode ser alcançado com o Operador de propagação :

const obj3 = { ...obj1, ...obj2 };
kulpae
fonte
54
ou, para evitar a sujeira obj1, use um objeto vazio como alvo:$.extend({}, obj1, obj2)
rymo 19/03/12
Sim, ao lidar com fechamentos e objetos passados ​​por referência, siga as orientações da rymo para evitar afetar o objeto original para operações subseqüentes.
Nathan Smith
2
Para navegadores modernos, verifique a solução usando @manfred usando Object.assign(), que não depende do JQuery se você ainda não estiver usando a biblioteca.
23418 Phil
Funciona, mas altera o primeiro parâmetro e é algo que você precisa estar ciente. Veja developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/…
kulpae
62

Agora, em 2016, eu diria que a melhor maneira / padrão é o Object.assign ()
Pure Javascript. Não é necessário jQuery.

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

Mais informações, exemplos e polyfill aqui:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign

Manfred
fonte
Qual é o tipo de obj3? e se obj1 for do tipo IABC, o obj3 manterá esse tipo posteriormente?
windmaomao
49

É assim que o Prototype faz:

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

chamado como, por exemplo:

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

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

EDIT : adicionada verificação hasOwnProperty () conforme corretamente apontada por bucabay nos comentários

Jonathan Fingland
fonte
6
Você precisará da fonte de teste.hasOwnProperty (propriedade) para certificar-se de copiar apenas as propriedades imediatas. Como é, este poderia copiará todas as propriedades, incluindo aqueles derivados de Object.prototype
bucabay
22

Mantenha simples...

function mergeArray(array1,array2) {
  for(item in array1) {
    array2[item] = array1[item];
  }
  return array2;
}
Chris Scerbo
fonte
6
Você verificar hasOwnProperty para evitar a cópia de quaisquer propriedades naarrau1.prototype
LeeGee
@LeeGee está certo. Você precisa desse hasOwnPropertycheque. Referir-se aos objetos como matrizes também é enganoso (eles são objetos), os nomes dos argumentos devem indicar que a matriz2 está mutada ou que um novo objeto deve ser retornado. Eu editaria a resposta, mas, na verdade, ela se tornaria quase exatamente a resposta editada de Jonathan Fingland, que já foi corrigida.
Vaz
14

O sublinhado também possui um método de extensão:

Copie todas as propriedades nos objetos de origem para o objeto de destino. Como está em ordem, a última fonte substituirá as propriedades com o mesmo nome nos argumentos anteriores.

_.extend(destination, *sources) 

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

No dojo , a "mesclagem" de 2 objetos / matrizes seria lang.mixin(destination, source)- você também pode misturar várias fontes em um destino, etc. - consulte a referência da função mixin para obter detalhes.

Alex Martelli
fonte
6

você deseja sobrescrever uma propriedade se os nomes forem iguais, mas os valores não forem?

E você deseja alterar permanentemente um dos objetos originais,

ou você deseja que um novo objeto mesclado seja retornado?

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

Rolando sua própria função de extensão / mixina

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???' });

...

Isso simplesmente estende uma série de objetos, recursivamente. Além disso, observe que essa função recursiva é TCO ( Tail-Call Optimized ), pois seu retorno é a última chamada para si mesma.

Além disso, você pode querer propriedades direcionadas . Nesse caso, convém condensar objetos com base em id,quantity ou outra propriedade. Essa abordagem pode ter um pequeno livro escrito sobre ela e requer justaposição de objetos e pode se tornar muito complexa. Eu escrevi uma pequena biblioteca para isso, que está disponível mediante solicitação.

Espero que isto ajude!

Cody
fonte
4
  1. Em Javascript, não há noção de matriz associativa, há objetos
  2. A única maneira de mesclar dois objetos é fazer um loop para suas propriedades e copiar ponteiros para seus valores que não são tipos primitivos e valores para tipos primitivos para outra instância
Sergey Ilinsky
fonte
8
Objetos em Javascript são implementados como matrizes associativas, então certamente existe a noção do mesmo
Dexygen
2

Em 2019, você tem 2 boas opções:

Atribuição de objeto [ doc ]

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

Propagação de objeto [ doc ]

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

O primeiro tende a ser mais seguro, mais padrão e polivalente. Um bom prós e contras aqui

VincentPerrin.com
fonte
1

A interface do usuário do Yahoo (YUI) também possui uma função auxiliar para isso:

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

jquery possui um booleano para cópia profunda. Você poderia fazer algo assim:

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

Além disso, você pode editar esta função para suportar n-matrizes para mesclar.

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

Então agora você pode fazer

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);
demosthenes
fonte
0

Eu precisava de uma fusão profunda de objetos. Então, todas as outras respostas não me ajudaram muito. _.extend e jQuery.extend se saem bem, a menos que você tenha uma matriz recursiva como eu. Mas não é tão ruim, você pode programá-lo em cinco minutos:

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

Para mesclar matrizes no jQuery e quanto a $ .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
fonte
0

Solução recursiva (estende também matrizes de objetos) + nulo verificado

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

Testes

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
fonte
Tentei usar isso com o Node JS, mas Object.getOwnPropertyNames is not a functiontravará o aplicativo.
Legoless 17/05/19
Estou usando isso com algum mecanismo v8 oldish dentro da extensão plv8 PostgreSQL. E funciona em navegadores. Basta encontrar uma maneira no seu mecanismo JS de fazer o equivalente a "hasOwnProperty". Então você pode reutilizar essa lógica.
Sscarduzio 17/05
0

Aqui está a melhor solução.

obj1.unshift.apply( obj1, obj2 );

Além disso, obj1pode crescer dentro de um loop sem nenhum problema. (digamos que obj2seja dinâmico)

bilen
fonte