Mesclando objetos (matrizes associativas)


147

Qual é a melhor maneira / padrão de mesclar duas matrizes associativas em JavaScript? Todo mundo faz isso rolando seu próprio forloop?


10
não há matrizes associativas no javascript btw, apenas objetos.
Gblazex

Crossref mesma pergunta no Perl: stackoverflow.com/questions/350018/…
dreftymac

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

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


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


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

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}


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

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!


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

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


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

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

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

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

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 }

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)

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.