Jak łączyć właściwości z wielu obiektów JavaScript

105

Szukam najlepszego sposobu „dodawania” wielu obiektów JavaScript (tablice asocjacyjne).

Na przykład, biorąc pod uwagę:

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

jaki jest najlepszy sposób obliczenia:

{ "one" : 1, "two" : 2, "three" : 3, "four" : 4, "five" : 5 }
Vlad
źródło
3
Uwaga semantyczna: pomimo składni [] nie są one w ogóle tablicami. Zamówienie nie jest tak naprawdę gwarantowane.
Álvaro González
1
Kolejność iteracji nie jest gwarantowana zgodnie ze standardami ECMA. Ale to sposób, w jaki jest implementowany w większości przeglądarek. (Od John Resig) To zachowanie jest jawnie nieokreślone w specyfikacji ECMAScript. W ECMA-262, sekcja 12.6.4: Mechanika wyliczania właściwości ... zależy od implementacji. Jednak specyfikacja różni się znacznie od implementacji. Wszystkie nowoczesne implementacje ECMAScript wykonują iterację po właściwościach obiektów w kolejności, w jakiej zostały zdefiniowane. Z tego powodu zespół Chrome uznał to za błąd i będzie go naprawiać.
Juan Mendes,

Odpowiedzi:

159

ECMAscript 6 wprowadzono, Object.assign()aby osiągnąć to natywnie w Javascript.

Metoda Object . assign () służy do kopiowania wartości wszystkich wyliczalnych właściwości własnych z jednego lub większej liczby obiektów źródłowych do obiektu docelowego. Zwróci obiekt docelowy.

Dokumentacja MDN dotycząca 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.assignjest obsługiwany w wielu nowoczesnych przeglądarkach, ale jeszcze nie we wszystkich. Użyj transpilera, takiego jak Babel i Traceur, aby wygenerować kompatybilny wstecz ES5 JavaScript.

filoxo
źródło
4
To jedno z najlepszych, jakie mogę powiedzieć. Ponieważ obecnie najczęściej używa się E6, najlepszą odpowiedzią jest Object. assign.
Vimalraj Selvam
6
Jeśli nie wiesz, ile obiektów należy scalić, ponieważ znajdują się one w tablicy, możesz je scalić w następujący sposób:Object.assign.apply({}, [{a: 1}, {b: 2}, ....])
Jeanluca Scaljeri
1
Alternatywą dla Object.assign.applyscalania tablicy obiektów jest użycie operatora spreadu:Object.assign( ...objects )
Spen
@Spen, który jest już uwzględniony jako odpowiedź na to pytanie. Nie widziałem korzyści z powielania go tutaj.
filoxo
po 4 godzinach ta odpowiedź rozwiązała mój problem w Angular 7. Dzięki za prostotę i dokładność odpowiedzi.
Żel
35

Możesz użyć jquery w $.extendten sposób:

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>

adiga
źródło
+1 Nawet jeśli nie używasz metody jQuery, możesz użyć ich kodu, aby pomóc w skonstruowaniu własnej (być może bardziej szczegółowej) implementacji.
tvanfosson
25
@Randal: Istnieje wiele dobrych powodów, dla których nie należy używać jQuery.
Tim Down
3
Edycja:d = $.extend({},a,b,c);
Bob Stein
34

To powinno wystarczyć:

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

Wynik:

{
  "one": 1,
  "two": 2,
  "three": 3,
  "four": 4,
  "five": 5
}
cletus
źródło
Nie lengthsprawdza rozmiaru tablicy przy każdym wywołaniu? Jestem tak przyzwyczajony do pisania, for (var i = 0, len = array.length; i < len; ++i)że nie mogę sobie przypomnieć, dlaczego zacząłem to robić.
tvanfosson
1
Tak, to jest poprawne. Lepszą wydajnością jest jednorazowe buforowanie długości. Ponieważ jednak rozmiar „tablicy” argumentów prawdopodobnie nigdy nie będzie bardzo duży, w tym przypadku nie będzie to miało większego znaczenia.
jhurshman
1
Nie, z wyjątkiem nieznacznie w IE6. Dostęp do właściwości length kosztuje tyle samo, co dostęp do zmiennej len.
Alsciende
1
@Juan Uważam, że się mylisz i zrobiłem kilka testów, aby się zdecydować. Buforowanie długości to łatwy mit optymalizacji, który był przestarzały przez wiele lat i sprawia, że ​​kod jest (nieco) mniej czytelny. W rzeczywistości buforowanie długości czasami spowalnia przeglądarkę (Safari).
Alsciende
2
Czy nie byłoby lepiej napisać 'for (var p in ...' zamiast 'for (p in ...'?
Hugo
24

ECMAScript 6 ma rozszerzoną składnię . A teraz możesz to zrobić:

const obj1 = { 1: 11, 2: 22 };
const obj2 = { 3: 33, 4: 44 };
const obj3 = { ...obj1, ...obj2 };

console.log(obj3); // {1: 11, 2: 22, 3: 33, 4: 44}

valex
źródło
12

Podkreślenie ma kilka metod, aby to zrobić;

1. _.extend (miejsce docelowe, * źródła)

Skopiuj wszystkie właściwości obiektów źródłowych do obiektu docelowego i zwróć obiekt docelowy .

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

Lub

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

2. _.defaults (obiekt, * domyślne)

Wypełnij niezdefiniowane właściwości obiektu wartościami z obiektów domyślnych i zwróć obiekt .

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

Lub

_.defaults(a, b);
=> {"one" : 1, "two" : 2, "three" : 3}
_.defaults(a, c);
=> {"one" : 1, "two" : 2, "three" : 3, "four" : 4, "five" : 5 }
gihanchanuka
źródło
4

Dlaczego funkcja powinna być ograniczona do 3 argumentów? Sprawdź także 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;
}
Alsciende
źródło
4

Płytkie klonowanie (z wyłączeniem prototypu) lub scalanie obiektów jest teraz możliwe przy użyciu krótszej składni niż Object. assign () .

Składnia spreadów dla literałów obiektowych została wprowadzona w 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 }

Operator Spread (...) jest obsługiwany w wielu nowoczesnych przeglądarkach, ale nie we wszystkich.

Dlatego zaleca się użycie transpilera, takiego jak Babel, do konwersji kodu ECMAScript 2015+ na wsteczną kompatybilną wersję JavaScript w obecnych i starszych przeglądarkach lub środowiskach.

Oto równoważny kod, który Babel wygeneruje dla Ciebie:

"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 }
Thiago Santos
źródło
To jest znacznie bardziej solidna odpowiedź niż zaakceptowana odpowiedź. Dzięki!
Kaleb Anderson
3
function Collect(a, b, c) {
    for (property in b)
        a[property] = b[property];

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

    return a;
}

Uwaga: Istniejące właściwości w poprzednich obiektach zostaną nadpisane.

Björn
źródło
2
Ma to efekt uboczny, który a === dna końcu. To może być w porządku, a może nie.
tvanfosson
3

Jest to łatwe przy użyciu operatora rozszerzania ES7 dla obiektu, w konsoli przeglądarki

({ name: "Alex", ...(true  ? { age: 19 } : { })}) // {name: "Alex", age: 19}
({ name: "Alex", ...(false ? { age: 19 } : { })}) // {name: "Alex",        }
Purkhalo Alex
źródło
1
Miły. To pytanie ma ponad 10 lat, cieszę się, że tak prosta obsługa jest teraz łatwa. Wcześniej nie było to takie łatwe, jeśli spojrzeć na odpowiedzi innych osób.
Vlad
dlatego zostawiłem własną odpowiedź :)
Purkhalo Alex
2

Aby scalić kilka dynamicznych obiektów, możemy skorzystać Object.assignze składni spread .

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

Powyższa funkcja akceptuje dowolną liczbę obiektów, łącząc wszystkie ich właściwości w nowy obiekt z właściwościami z późniejszych obiektów nadpisując te z poprzednich obiektów.

Próbny:

Aby połączyć tablicę obiektów, można zastosować podobną metodę.

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

Próbny:

hev1
źródło
1

Prawdopodobnie najszybszym, wydajnym i bardziej ogólnym sposobem jest to (możesz scalić dowolną liczbę obiektów, a nawet skopiować do pierwszego -> przypisz):

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

Pozwala także na modyfikację pierwszego obiektu, który jest przekazywany przez odniesienie. Jeśli nie chcesz tego, ale chcesz mieć zupełnie nowy obiekt zawierający wszystkie właściwości, możesz przekazać {} jako pierwszy argument.

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

Połączony_obiekt i obiekt1 zawierają właściwości obiekt1, obiekt2, obiekt3.

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

W tym przypadku połączony_obiekt zawiera właściwości obiektu1, obiekt2, obiekt3, ale obiekt1 nie jest modyfikowany.

Sprawdź tutaj: https://jsfiddle.net/ppwovxey/1/

Uwaga: obiekty JavaScript są przekazywane przez odniesienie.

Selay
źródło
1

ES6 ++

Pytanie jest dodawanie różnych różnych obiektów w jeden.

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

powiedzmy, że masz jeden obiekt z wieloma kluczami, które są obiektami:

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

to było rozwiązanie, które znalazłem (nadal muszę na zawsze: /)

let objAll = {};

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

W ten sposób możemy dynamicznie dodawać WSZYSTKIE klucze obiektów do jednego.

// Output => { bar: 'foo', foo: 'bar' }
Joseph Briggs
źródło
0

Najprostsze: operatory rozproszenia

var obj1 = {a: 1}
var obj2 = {b: 2}
var concat = { ...obj1, ...obj2 } // { a: 1, b: 2 }
Rodolfo Paranhos
źródło
-2
function collect(a, b, c){
    var d = {};

    for(p in a){
        d[p] = a[p];
    }
    for(p in b){
        d[p] = b[p];
    }
    for(p in c){
        d[p] = c[p];
    }

    return d;
}
Álvaro González
źródło