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


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

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


Какой тип для 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}


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

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или другое имущество. При таком подходе может быть написана небольшая книга, которая требует сопоставления объектов и может быть очень сложной. Я написал небольшую библиотеку для этого, которая доступна по запросу.

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


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

8
Объекты в Javascript реализованы в виде ассоциативных массивов, так что, безусловно, существует такое же понятие
— Dexygen

2

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

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

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

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

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

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



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

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 }

Я попытался использовать это с Node JS, но Object.getOwnPropertyNames is not a functionпроизойдет сбой приложения.
— Legoless

Я использую это со старым движком v8 в расширении PostgreSQL plv8. И это работает в браузерах. Просто найдите способ в вашем движке JS сделать то же самое, что и hasOwnProperty. Тогда вы можете повторно использовать эту логику.
— sscarduzio

0

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

obj1.unshift.apply( obj1, obj2 );

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.