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 }

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.


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>


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

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}


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 }

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

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 }

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.


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",        }

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:


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.


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

0

Najprostsze: operatory rozproszenia

var obj1 = {a: 1}
var obj2 = {b: 2}
var concat = { ...obj1, ...obj2 } // { a: 1, b: 2 }

-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;
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.