Объединение объектов (ассоциативные массивы)

147

Какой лучший / стандартный способ объединения двух ассоциативных массивов в JavaScript? Все просто делают это, вращая свой собственный forцикл?

Вильгельм
источник
10
в JavaScript нет ассоциативных массивов, только объекты.
gblazex
Перекрестный же вопрос в Perl: stackoverflow.com/questions/350018/…
dreftymac
Ассоциативные массивы в javascript: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Крис Мартин,

Ответы:

204

с JQuery вы можете позвонить $.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

(ассоциированные массивы - это объекты в js)

смотрите здесь: http://api.jquery.com/jQuery.extend/


редактировать: как предложено rymo, лучше сделать это следующим образом:

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

Поскольку здесь obj1 (и obj2) остаются без изменений.


edit2: в 2018 году способ сделать это через Object.assign:

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

При работе с ES6 этого можно достичь с помощью оператора Spread :

const obj3 = { ...obj1, ...obj2 };
kulpae
источник
54
или чтобы избежать взлома obj1, используйте пустой объект в качестве цели:$.extend({}, obj1, obj2)
rymo
Да, при работе с замыканиями и объектами, переданными по ссылке, воспользуйтесь рекомендациями rymo, чтобы избежать влияния на исходный объект для последующих операций.
Натан Смит
2
Для современных браузеров, проверьте решение с помощью @manfred using Object.assign(), который не полагается на JQuery, если вы еще не используете библиотеку.
Фил
Это работает, но это изменяет первый параметр, и это то, что вам нужно знать. См developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/...
kulpae
62

Сейчас, в 2016 году, я бы сказал, что лучшим / стандартным способом является Object.assign ()
Pure Javascript. Нет JQuery не требуется.

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

Больше информации, примеров и полифилла здесь:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign

Manfred
источник
Какой тип для obj3? Что, если obj1 - это тип IABC, будет ли потом obj3 сохранять этот тип?
windmaomao
49

Вот как это делает Prototype:

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

называется как, например:

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

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

РЕДАКТИРОВАТЬ : добавлена ​​проверка hasOwnProperty (), правильно указанная bucabay в комментариях

Джонатан Финглэнд
источник
6
Вам понадобится тест source.hasOwnProperty (property), чтобы убедиться, что вы скопировали только непосредственные свойства. Таким образом, он может скопировать все свойства, включая свойства, полученные из Object.prototype
bucabay
22

Будь проще...

function mergeArray(array1,array2) {
  for(item in array1) {
    array2[item] = array1[item];
  }
  return array2;
}
Крис Щербо
источник
6
Вы должны проверить hasOwnProperty, чтобы избежать копирования каких-либо свойств наarrau1.prototype
LeeGee
@ LeeGee прав. Тебе нужен этот hasOwnPropertyчек. Обращение к объектам как к массивам также вводит в заблуждение (они являются объектами), имена аргументов должны указывать на то, что массив2 мутирован или должен быть возвращен новый объект. Я бы отредактировал ответ, но фактически он стал бы почти точно отредактированным ответом Джонатана Фингланда, который уже был исправлен.
Ваз
14

У подчеркивания также есть метод расширения:

Скопируйте все свойства в исходных объектах в целевой объект. Это по порядку, поэтому последний источник переопределит свойства с тем же именем в предыдущих аргументах.

_.extend(destination, *sources) 

_.extend({name : 'moe'}, {age : 50});
=> {name : 'moe', age : 50}
Майк Маккей
источник
7

В dojo 2-объекты / массивы «объединяются» lang.mixin(destination, source)- вы также можете смешивать несколько источников в одном месте назначения и т. Д. - подробности см. В справочнике функции mixin .

Алекс Мартелли
источник
6

Вы хотите перезаписать свойство, если имена совпадают, а значения не совпадают?

И вы хотите навсегда изменить один из оригинальных объектов,

или вы хотите вернуть новый объединенный объект?

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
источник
6

Прокручиваем собственную функцию расширения / микширования

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

...

Это просто расширяет восклицательный знак объектов, рекурсивно. Кроме того, обратите внимание, что эта рекурсивная функция является TCO ( Tail-Call Optimized ), поскольку ее возвращение является последним вызовом к себе.

Кроме того, вы можете захотеть целевые свойства . В этом случае, вы можете конденсироваться объектами , основанных на id, quantityили другое имущество. При таком подходе может быть написана небольшая книга, которая требует сопоставления объектов и может быть очень сложной. Я написал небольшую библиотеку для этого, которая доступна по запросу.

Надеюсь это поможет!

Cody
источник
4
  1. В Javascript нет понятия ассоциативного массива, есть объекты
  2. Единственный способ объединить два объекта - выполнить цикл для их свойств и скопировать указатели на их значения, которые не являются примитивными типами, и значения для примитивных типов в другой экземпляр.
Сергей Ильинский
источник
8
Объекты в Javascript реализованы в виде ассоциативных массивов, так что, безусловно, существует такое же понятие
Dexygen
2

В 2019 году у вас есть 2 хороших варианта:

Назначение объекта [ doc ]

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

Распространение объектов [ doc ]

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

Первый имеет тенденцию быть более безопасным, более стандартным и поливалентным. Хорошие плюсы и минусы здесь

VincentPerrin.com
источник
1

Yahoo UI (YUI) также имеет вспомогательную функцию для этого:

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 имеет логическое значение для глубокого копирования. Вы могли бы сделать что-то подобное:

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

Также вы можете редактировать эту функцию для поддержки объединения n-массивов.

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

Так что теперь вы можете сделать

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

Мне нужно было глубокое слияние объектов. Так что все остальные ответы мне не очень помогли. _.extend и jQuery.extend работают хорошо, если у вас нет рекурсивного массива, как у меня. Но все не так плохо, вы можете запрограммировать его за пять минут:

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

Чтобы объединить массивы в jQuery, а как насчет $ .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
источник
0

Рекурсивное решение (расширяет также массивы объектов) + проверено ноль

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

тесты

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
источник
Я попытался использовать это с Node JS, но Object.getOwnPropertyNames is not a functionпроизойдет сбой приложения.
Legoless
Я использую это со старым движком v8 в расширении PostgreSQL plv8. И это работает в браузерах. Просто найдите способ в вашем движке JS сделать то же самое, что и hasOwnProperty. Тогда вы можете повторно использовать эту логику.
sscarduzio
0

Вот лучшее решение.

obj1.unshift.apply( obj1, obj2 );

Кроме того, obj1может расти внутри цикла без каких-либо проблем. (скажем obj2, динамический)

Bilen
источник