Fusionar objetos (matrices asociativas)

Respuestas:

204

con jquery puedes llamar $.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

(las matrices asociadas son objetos en js)

mira aquí: http://api.jquery.com/jQuery.extend/


editar: como sugirió rymo, es mejor hacerlo de esta manera:

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

Como aquí, obj1 (y obj2) permanecen sin cambios.


edit2: en 2018 la forma de hacerlo es a través de Object.assign:

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

Si trabaja con ES6, esto se puede lograr con el Operador de propagación :

const obj3 = { ...obj1, ...obj2 };
kulpae
fuente
54
o para evitar limpiando obj1, utilice un objeto vacío como objetivo:$.extend({}, obj1, obj2)
Rymo
Sí, cuando se trata de cierres y objetos pasados ​​por referencia, siga los consejos de rymo para evitar afectar el objeto original para operaciones posteriores.
Nathan Smith
2
Para los navegadores modernos, verifique la solución usando @manfred usando Object.assign(), que no depende de JQuery si aún no está usando la biblioteca.
Phil
Funciona, pero altera el primer parámetro y eso es algo que debe tener en cuenta. Ver developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
kulpae
49

Así es como Prototype lo hace:

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

llamado como, por ejemplo:

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

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

EDITAR : se ha agregado la verificación hasOwnProperty () como lo señala correctamente bucabay en los comentarios

Jonathan Fingland
fuente
66
Necesitará la prueba source.hasOwnProperty (propiedad) para asegurarse de que solo copia las propiedades inmediatas. Como es, esto podría copiará todos propiedades, incluyendo las derivadas de Object.prototype
bucabay
22

Mantenlo simple...

function mergeArray(array1,array2) {
  for(item in array1) {
    array2[item] = array1[item];
  }
  return array2;
}
Chris Scerbo
fuente
66
Debe verificar hasOwnProperty para evitar copiar cualquier propiedad en elarrau1.prototype
LeeGee
@LeeGee tiene razón. Necesitas ese hasOwnPropertycheque. Hacer referencia a los objetos como matrices también es engañoso (son objetos), los nombres arg deben transmitir que la matriz2 está mutada o que se debe devolver un nuevo objeto. Editaría la respuesta, pero en efecto se convertiría casi exactamente en la respuesta editada de Jonathan Fingland que ya se ha corregido.
Vaz
14

El subrayado también tiene un método extendido:

Copie todas las propiedades en los objetos de origen al objeto de destino. Está en orden, por lo que la última fuente anulará las propiedades del mismo nombre en argumentos anteriores.

_.extend(destination, *sources) 

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

En dojo , la combinación de 2 objetos / matrices sería lang.mixin(destination, source): también puede mezclar múltiples fuentes en un destino, etc., consulte la referencia de la función mixin para obtener más detalles.

Alex Martelli
fuente
6

¿Desea sobrescribir una propiedad si los nombres son iguales pero los valores no lo son?

¿Y desea cambiar permanentemente uno de los objetos originales,

o quieres que se devuelva un nuevo objeto combinado?

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

Rolling Your Own Extend / Mixin Function

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

...

Esto simplemente se extiende un splat de objetos, de forma recursiva. Además, tenga en cuenta que esta función recursiva es TCO ( Tail-Call Optimized ) ya que su retorno es la última llamada a sí mismo.

Además, es posible que desee propiedades específicas . En este caso, es posible que desee para condensar los objetos basados en id, quantityo de otra propiedad. Este enfoque podría tener un pequeño libro escrito sobre él y requiere yuxtaposición de objetos y puede volverse muy complejo. He escrito una pequeña biblioteca para esto que está disponible a pedido.

¡Espero que esto ayude!

Cody
fuente
4
  1. En Javascript no existe la noción de matriz asociativa, hay objetos
  2. La única forma de fusionar dos objetos es hacer un bucle para sus propiedades y copiar punteros a sus valores que no son tipos primitivos y valores para tipos primitivos a otra instancia
Sergey Ilinsky
fuente
8
Los objetos en Javascript se implementan como matrices asociativas, por lo que ciertamente existe la noción de lo mismo
Dexygen
2

En 2019 tienes 2 buenas opciones:

Asignación de objeto [ doc ]

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

Difusión de objetos [ doc ]

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

El primero tiende a ser más seguro, más estándar y polivalente. Un buen pros y contras aquí

VincentPerrin.com
fuente
1

Yahoo UI (YUI) también tiene una función auxiliar para esto:

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

jquery tiene un booleano para copia profunda. Podrías hacer algo así:

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

También puede editar esta función para admitir n-arrays para fusionar.

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

Entonces ahora puedes hacer

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);
Demóstenes
fuente
0

Necesitaba una fusión profunda de objetos. Así que todas las otras respuestas no me ayudaron mucho. _.extend y jQuery.extend funcionan bien, a menos que tenga una matriz recursiva como yo. Pero no es tan malo, puedes programarlo en 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
fuente
0

Para fusionar matrices en jQuery, ¿qué pasa con $ .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
fuente
0

Solución recursiva (se extiende también matrices 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;
};

Pruebas

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
fuente
Intenté usar esto con Node JS, pero Object.getOwnPropertyNames is not a functionbloqueare la aplicación.
Legoless
Estoy usando esto con algún motor v8 antiguo dentro de la extensión plv8 PostgreSQL. Y funciona en navegadores. Simplemente encuentre una manera en su motor JS para hacer lo equivalente a "hasOwnProperty". Entonces puedes reutilizar esta lógica.
sscarduzio
0

Aquí está la mejor solución.

obj1.unshift.apply( obj1, obj2 );

Además, obj1puede crecer dentro de un bucle sin ningún problema. (digamos que obj2es dinámico)

bilen
fuente