Fusionar objetos (matrices asociativas)


147

¿Cuál es la mejor / forma estándar de fusionar dos matrices asociativas en JavaScript? ¿Todos lo hacen rodando su propio forbucle?


10
no hay matrices asociativas en javascript por cierto, solo objetos.
gblazex

Crossref la misma pregunta en Perl: stackoverflow.com/questions/350018/…
dreftymac

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

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


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

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}


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

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!


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

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í



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

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

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

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 }

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)

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.