Как объединить свойства из нескольких объектов JavaScript


105

Я ищу лучший способ «добавить» несколько объектов JavaScript (ассоциативных массивов).

Например, учитывая:

a = { "one" : 1, "two" : 2 };
b = { "three" : 3 };
c = { "four" : 4, "five" : 5 };

как лучше всего вычислить:

{ "one" : 1, "two" : 2, "three" : 3, "four" : 4, "five" : 5 }

3
Семантическое примечание: несмотря на синтаксис [], они вовсе не массивы. Порядок на самом деле не гарантируется.
Альваро Гонсалес,

1
Порядок итераций не гарантируется в соответствии со стандартами ECMA. Но именно так это реализовано в большинстве браузеров. (От Джона Ресига) Это поведение явно не определено спецификацией ECMAScript. В ECMA-262, раздел 12.6.4: Механика перечисления свойств ... зависит от реализации. Однако спецификация сильно отличается от реализации. Все современные реализации ECMAScript перебирают свойства объекта в том порядке, в котором они были определены. Из-за этого команда Chrome сочла это ошибкой и будет исправлять ее.
Хуан Мендес,

Ответы:


162

ECMAscript 6 введен Object.assign()для достижения этой цели изначально в Javascript.

Метод Object.assign () используется для копирования значений всех перечислимых собственных свойств из одного или нескольких исходных объектов в целевой объект. Он вернет целевой объект.

Документация MDN по Object.assign ()

var o1 = { a: 1 };
var o2 = { b: 2 };
var o3 = { c: 3 };

var obj = Object.assign({}, o1, o2, o3);
console.log(obj); // { a: 1, b: 2, c: 3 }

Object.assignподдерживается во многих современных браузерах, но еще не во всех. Используйте транспилятор, такой как Babel и Traceur, для создания обратно совместимого кода ES5 JavaScript.


4
Это одно из лучших, что я могу сказать. Поскольку сейчас в основном используется E6, Object.assign - лучший ответ.
Vimalraj Selvam

6
Если вы не знаете, сколько объектов нужно объединить, поскольку они находятся в массиве, вы можете объединить их следующим образом:Object.assign.apply({}, [{a: 1}, {b: 2}, ....])
Жанлука Скальери

1
Альтернативой Object.assign.applyобъединению массива объектов является использование оператора распространения:Object.assign( ...objects )
Spen,

@Spen, который уже включен в качестве ответа на этот вопрос. Я не вижу пользы в дублировании этого здесь.
filoxo 06

через 4 часа этот ответ решил мою проблему в Angular 7. Спасибо за простоту и точность ответа.
Gel

35

Вы можете использовать jquery $.extendтак:

let a = { "one" : 1, "two" : 2 },
    b = { "three" : 3 },
    c = { "four" : 4, "five" : 5 };

let d = $.extend({}, a, b, c)

console.log(d)
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>


+1 Даже если вы не используете метод jQuery, вы можете использовать их код, чтобы помочь построить свою собственную (возможно, более конкретную) реализацию.
tvanfosson

25
@Randal: Есть много вполне веских причин не использовать jQuery.
Тим Даун

3
Редактировать:d = $.extend({},a,b,c);
Боб Штейн

34

Это должно сделать это:

function collect() {
  var ret = {};
  var len = arguments.length;
  for (var i = 0; i < len; i++) {
    for (p in arguments[i]) {
      if (arguments[i].hasOwnProperty(p)) {
        ret[p] = arguments[i][p];
      }
    }
  }
  return ret;
}

let a = { "one" : 1, "two" : 2 };
let b = { "three" : 3 };
let c = { "four" : 4, "five" : 5 };

let d = collect(a, b, c);
console.log(d);

Вывод:

{
  "one": 1,
  "two": 2,
  "three": 3,
  "four": 4,
  "five": 5
}

Не lengthпроверяет размер массива при каждом вызове? Я так привык писать, for (var i = 0, len = array.length; i < len; ++i)что даже не могу вспомнить, почему я начал это делать.
tvanfosson

1
Да, это правильно. Лучше кэшировать длину один раз. Однако, поскольку размер «массива» аргументов вряд ли когда-либо будет очень большим, в данном случае это не имеет особого значения.
jhurshman

1
Нет, разве что в IE6. Доступ к свойству length стоит так же, как доступ к переменной len.
Alsciende,

1
@Juan Я считаю, что вы ошибаетесь, и я сделал несколько тестов, чтобы принять решение. Кэширование длины - это простой миф об оптимизации, который устарел уже много лет и делает код (немного) менее читаемым. Собственно, кеширование длины иногда тормозит браузер (Safari).
Alsciende,

2
Разве не лучше было бы написать «for (var p in ...» вместо «for (p in ...»?)
Хьюго


12

У Underscore есть несколько способов сделать это;

1. _.extend (назначение, * источники)

Скопируйте все свойства исходных объектов в целевой объект и верните целевой объект.

_.extend(a, _.extend(b, c));
=> {"one" : 1, "two" : 2, "three" : 3, "four" : 4, "five" : 5 }

Или

_.extend(a, b);
=> {"one" : 1, "two" : 2, "three" : 3}
_.extend(a, c);
=> {"one" : 1, "two" : 2, "three" : 3, "four" : 4, "five" : 5 }

2. _.defaults (объект, * по умолчанию)

Заполните неопределенные свойства в объекте значениями из объектов по умолчанию и верните объект .

_.defaults(a, _.defaults(b, c));
=> {"one" : 1, "two" : 2, "three" : 3, "four" : 4, "five" : 5 }

Или

_.defaults(a, b);
=> {"one" : 1, "two" : 2, "three" : 3}
_.defaults(a, c);
=> {"one" : 1, "two" : 2, "three" : 3, "four" : 4, "five" : 5 }

4

Почему функция должна быть ограничена 3 аргументами? Также проверьте наличие hasOwnProperty.

function Collect() {
    var o={};
    for(var i=0;i<arguments.length;i++) {
      var arg=arguments[i];
      if(typeof arg != "object") continue;
      for(var p in arg) {
        if(arg.hasOwnProperty(p)) o[p] = arg[p];
      }
    }
    return o;
}

4

Поверхностное клонирование (за исключением прототипа) или слияние объектов теперь возможно с использованием более короткого синтаксиса, чем Object.assign () .

Синтаксис распространения для объектных литералов был введен в ECMAScript 2018 ):

const a = { "one": 1, "two": 2 };
const b = { "three": 3 };
const c = { "four": 4, "five": 5 };

const result = {...a, ...b, ...c};
// Object { "one": 1, "two": 2 , "three": 3, "four": 4, "five": 5 }

Оператор распространения (...) поддерживается во многих современных браузерах, но не во всех.

Поэтому рекомендуется использовать транспилятор, такой как Babel, для преобразования кода ECMAScript 2015+ в обратно совместимую версию JavaScript в текущих и более старых браузерах или средах.

Это эквивалентный код, который Babel сгенерирует для вас:

"use strict";

var _extends = Object.assign || function(target) {
  for (var i = 1; i < arguments.length; i++) {
    var source = arguments[i];
    for (var key in source) {
      if (Object.prototype.hasOwnProperty.call(source, key)) {
        target[key] = source[key];
      }
    }
  }
  return target;
};

var a = { "one": 1, "two": 2 };
var b = { "three": 3 };
var c = { "four": 4, "five": 5 };

var result = _extends({}, a, b, c);
// Object { "one": 1, "two": 2 , "three": 3, "four": 4, "five": 5 }

Это гораздо более надежный ответ, чем принятый ответ. Спасибо!
Калеб Андерсон

3
function Collect(a, b, c) {
    for (property in b)
        a[property] = b[property];

    for (property in c)
        a[property] = c[property];

    return a;
}

Примечание: существующие свойства в предыдущих объектах будут перезаписаны.


2
Это имеет побочный эффект, что a === dв конце. Это может быть нормально, а может и нет.
tvanfosson

3

Оператор распространения ES7 для объекта легко использовать , в консоли браузера введите

({ name: "Alex", ...(true  ? { age: 19 } : { })}) // {name: "Alex", age: 19}
({ name: "Alex", ...(false ? { age: 19 } : { })}) // {name: "Alex",        }

1
Ницца. Этому вопросу больше 10 лет, рад, что такая простая операция теперь стала легкой. Раньше это было не так просто, если вы посмотрите на ответы других людей.
Влад

поэтому оставил свой ответ :)
Пурхало Алексей

2

Чтобы объединить динамическое количество объектов, мы можем использовать синтаксисObject.assign с расширением .

const mergeObjs = (...objs) => Object.assign({}, ...objs);

Вышеупомянутая функция принимает любое количество объектов, объединяя все их свойства в новый объект со свойствами из более поздних объектов, перезаписывая свойства из предыдущих объектов.

Демо:

Для объединения массива объектов можно применить аналогичный метод.

const mergeArrayOfObjs = arr => Object.assign({}, ...arr);

Демо:


1

Вероятно, самый быстрый, эффективный и более общий способ - это (вы можете объединить любое количество объектов и даже скопировать в первый -> назначить):

function object_merge(){
    for (var i=1; i<arguments.length; i++)
       for (var a in arguments[i])
         arguments[0][a] = arguments[i][a];
   return arguments[0];
}

Это также позволяет вам изменять первый объект, переданный по ссылке. Если вы этого не хотите, но хотите иметь полностью новый объект, содержащий все свойства, вы можете передать {} в качестве первого аргумента.

var object1={a:1,b:2};
var object2={c:3,d:4};
var object3={d:5,e:6};
var combined_object=object_merge(object1,object2,object3); 

Combined_object и object1 оба содержат свойства object1, object2, object3.

var object1={a:1,b:2};
var object2={c:3,d:4};
var object3={d:5,e:6};
var combined_object=object_merge({},object1,object2,object3); 

В этом случае Combined_object содержит свойства object1, object2, object3, но object1 не изменяется.

Проверьте здесь: https://jsfiddle.net/ppwovxey/1/

Примечание: объекты JavaScript передаются по ссылке.


1

ES6 ++

Вопрос в том, чтобы объединить разные объекты в один.

let obj = {};
const obj1 = { foo: 'bar' };
const obj2 = { bar: 'foo' };
Object.assign(obj, obj1, obj2);
//output => {foo: 'bar', bar: 'foo'};

Допустим, у вас есть один объект с несколькими ключами, которые являются объектами:

let obj = {
  foo: { bar: 'foo' },
  bar: { foo: 'bar' }
}

это было решение, которое я нашел (все еще нужно foreach: /)

let objAll = {};

Object.values(obj).forEach(o => {
  objAll = {...objAll, ...o};
});

Делая это, мы можем динамически добавлять ВСЕ ключи объекта в один.

// Output => { bar: 'foo', foo: 'bar' }


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