Об’єднання об'єктів (асоціативні масиви)


147

Який найкращий / стандартний спосіб об’єднання двох асоціативних масивів у JavaScript? Чи всі це роблять, прокручуючи власну forпетлю?


10
в javascript btw немає асоціативних масивів, лише об'єкти.
gblazex

CrossRef ж питання в 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

Так, під час роботи із закриттями та об'єктами, переданими посиланням, дотримуйтесь порад римо, щоб не впливати на оригінальний об'єкт для наступних операцій.
Натан Сміт

2
Для сучасних веб-переглядачів перевірте рішення за допомогою @manfred Object.assign(), яке не покладається на JQuery, якщо ви вже не використовуєте бібліотеку.
Філ

Це працює, але він змінює перший параметр, і це те, що вам потрібно знати. Дивіться developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
kulpae

62

Зараз у 2016 році я б сказав, що найкращий / стандартний спосіб - Object.assign ()
Чистий 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

Ось як це робить прототип:

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

EDIT : додано перевірку hasOwnProperty (), як правильно вказав bucabay в коментарях


6
Вам знадобиться тестовий source.hasOwnProperty (властивість), щоб переконатися, що ви копіюєте лише безпосередні властивості. Так, це може скопіювати всі властивості, включаючи ті, що походять від 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перевірка. Посилання на об'єкти як масиви також вводить в оману (вони є об'єктами), назви аргументів повинні свідчити про те, що array2 мутується або новий об’єкт повинен бути повернутий. Я би відредагував відповідь, але фактично це стане майже точно відредагованою відповіддю Джонатана Фінгленда, яка вже виправлена.
Ваз

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

Рухомий свої власні Extend / підмішати функції

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додаток перестане працювати.
Безголосий

Я використовую це за допомогою старовинного двигуна v8 в межах розширення plv8 PostgreSQL. І це працює в браузерах. Просто знайдіть у своєму двигуні JS спосіб зробити еквівалентну річ, як "hasOwnProperty". Тоді ви можете повторно використовувати цю логіку.
sscarduzio

0

Ось найкраще рішення.

obj1.unshift.apply( obj1, obj2 );

Також obj1може рости всередині циклу без проблем. (скажімо, obj2це динамічно)

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.