Scalanie obiektów (tablice asocjacyjne)

147

Jaki jest najlepszy / standardowy sposób łączenia dwóch tablic asocjacyjnych w JavaScript? Czy każdy po prostu robi to, obracając własną forpętlę?

Wilhelm
źródło
10
btw nie ma tablic asocjacyjnych, są tylko obiekty.
gblazex,
Crossref to samo pytanie w Perlu: stackoverflow.com/questions/350018/…
dreftymac
Tablice asocjacyjne w javascript: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/ ...
Chris Martin

Odpowiedzi:

204

z jQuery możesz zadzwonić $.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

(asoc. tablice są obiektami w js)

spójrz tutaj: http://api.jquery.com/jQuery.extend/


edycja: Jak sugerował rymo, lepiej zrobić to w ten sposób:

obj3 = $.extend({}, obj1, obj2); 
obj3 == {a: 4, b: 2, c: 110}

Tak jak tutaj obj1 (i obj2) pozostają niezmienione.


edit2: W 2018 roku można to zrobić poprzez Object.assign:

var obj3 = Object.assign({}, obj1, obj2); 
obj3 === {a: 4, b: 2, c: 110} // Pseudo JS

Jeśli pracujesz z ES6, można to osiągnąć za pomocą operatora rozprzestrzeniania :

const obj3 = { ...obj1, ...obj2 };
kulpae
źródło
54
lub aby uniknąć bałaganu obj1, użyj pustego obiektu jako celu:$.extend({}, obj1, obj2)
rymo
Tak, kiedy mamy do czynienia z zamknięciami i obiektami przekazywanymi przez odniesienie, należy posłużyć się radą rymo, aby nie wpływać na oryginalny obiekt podczas kolejnych operacji.
Nathan Smith
2
W przypadku nowoczesnych przeglądarek sprawdź rozwiązanie @manfred using Object.assign(), które nie polega na JQuery, jeśli nie używasz jeszcze biblioteki.
Phil
Działa, ale zmienia pierwszy parametr i to jest coś, o czym musisz być świadomy. Zobacz developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
kulpae
62

Teraz, w 2016 roku, powiedziałbym, że najlepszym / standardowym sposobem jest Object. assign ()
Pure Javascript. Nie jest potrzebne jQuery.

obj1 = {a: 1, b: 2};
obj2 = {a: 4, c: 110};
obj3 = Object.assign({},obj1, obj2);  // Object {a: 4, b: 2, c: 110}

Więcej informacji, przykładów i polyfill tutaj: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/ assign

Manfred
źródło
Jaki jest typ obj3? co jeśli obj1 jest typem IABC, czy obj3 zachowa ten typ później?
windmaomao
49

Tak to robi Prototype:

Object.extend = function(destination, source) {
    for (var property in source) {
        if (source.hasOwnProperty(property)) {
            destination[property] = source[property];
        }
    }
    return destination;
};

nazywany na przykład:

var arr1 = { robert: "bobby", john: "jack" };
var arr2 = { elizabeth: "liz", jennifer: "jen" };

var shortnames = Object.extend(arr1,arr2);

EDYCJA : dodano kontrolę hasOwnProperty (), jak poprawnie wskazał bucabay w komentarzach

Jonathan Fingland
źródło
6
Będziesz potrzebować testowego source.hasOwnProperty (właściwość), aby mieć pewność, że kopiujesz tylko bezpośrednie właściwości. W rzeczywistości mogłoby to skopiować wszystkie właściwości, w tym te pochodzące z Object.prototype
bucabay
22

Nie komplikuj...

function mergeArray(array1,array2) {
  for(item in array1) {
    array2[item] = array1[item];
  }
  return array2;
}
Chris Scerbo
źródło
6
Musisz sprawdzić hasOwnProperty, aby uniknąć kopiowania jakichkolwiek właściwości naarrau1.prototype
LeeGee
@LeeGee ma rację. Potrzebujesz tego hasOwnPropertyczeku. Odwołując się do obiektów, ponieważ tablice również wprowadzają w błąd (są to obiekty), nazwy argumentów powinny wskazywać, że tablica2 jest zmutowana lub że należy zwrócić nowy obiekt. Zmieniłbym odpowiedź, ale w efekcie stałaby się ona prawie dokładnie zredagowaną odpowiedzią Jonathana Finglanda, która została już poprawiona.
Vaz
14

Podkreślenie ma również metodę rozszerzania:

Skopiuj wszystkie właściwości obiektów źródłowych do obiektu docelowego. Jest w porządku, więc ostatnie źródło zastąpi właściwości o tej samej nazwie w poprzednich argumentach.

_.extend(destination, *sources) 

_.extend({name : 'moe'}, {age : 50});
=> {name : 'moe', age : 50}
Mike McKay
źródło
7

W dojo „scalanie” 2 obiektów / tablic wyglądałoby następująco lang.mixin(destination, source)- możesz także mieszać wiele źródeł w jednym miejscu docelowym itp. - szczegóły znajdziesz w opisie funkcji mixin .

Alex Martelli
źródło
6

czy chcesz nadpisać właściwość, jeśli nazwy są takie same, ale wartości nie?

Czy chcesz trwale zmienić jeden z oryginalnych obiektów,

czy chcesz, aby nowy scalony obiekt został zwrócony?

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];
            }
        }
    }
}
kennebec
źródło
6

Rolling własną wysuwania / mixin funkcji

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???' });

...

To po prostu rekurencyjnie rozciąga zbiór obiektów. Należy również zauważyć, że ta rekurencyjna funkcja to TCO ( Tail-Call Optimized ), ponieważ jej zwrot jest ostatnim wywołaniem samej siebie.

Ponadto możesz chcieć wybrać właściwości docelowe . W tym przypadku, można skondensować obiektów w oparciu id,quantity lub innego mienia. Takie podejście może wymagać napisania małej książki, wymaga zestawienia obiektów i może być bardzo złożone. Napisałem do tego małą bibliotekę, która jest dostępna na żądanie.

Mam nadzieję że to pomoże!

Cody
źródło
4
  1. W Javascript nie ma pojęcia tablicy asocjacyjnej, są obiekty
  2. Jedynym sposobem scalenia dwóch obiektów jest zapętlenie ich właściwości i skopiowanie wskaźników do ich wartości, które nie są typami pierwotnymi, oraz wartości typów pierwotnych do innej instancji
Sergey Ilinsky
źródło
8
Obiekty w Javascript są implementowane jako tablice asocjacyjne, więc z pewnością istnieje pojęcie tego samego
Dexygen
2

W 2019 roku masz 2 dobre opcje:

Przypisywanie obiektów [ doc ]

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

Rozprzestrzenianie obiektów [ doc ]

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

Pierwszy z nich wydaje się być bezpieczniejszy, bardziej standardowy i wszechstronny. A dobre plusy i minusy tutaj

VincentPerrin.com
źródło
1

Yahoo UI (YUI) ma również do tego funkcję pomocniczą:

http://developer.yahoo.com/yui/examples/yahoo/yahoo_merge.html

YAHOO.namespace('example');

YAHOO.example.set1 = { foo : "foo" };
YAHOO.example.set2 = { foo : "BAR", bar : "bar" };
YAHOO.example.set3 = { foo : "FOO", baz : "BAZ" };

var Ye = YAHOO.example;

var merged = YAHOO.lang.merge(Ye.set1, Ye.set2, Ye.set3);
Ben Walding
źródło
1

jquery ma wartość logiczną dla głębokiego kopiowania. Możesz zrobić coś takiego:

MergeRecursive = function(arr1, arr2){
    $.extend(true, arr1, arr2);
    return arr1;                
};

Możesz również edytować tę funkcję, aby obsługiwała n-tablice do scalania.

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

Więc teraz możesz to zrobić

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);
demostenes
źródło
0

Potrzebowałem głębokiego scalania obiektów. Więc wszystkie inne odpowiedzi nie pomogły mi zbytnio. _.extend i jQuery.extend radzą sobie dobrze, chyba że masz tablicę rekurencyjną, tak jak ja. Ale nie jest tak źle, możesz to zaprogramować w pięć minut:

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;
}
Rasmus
źródło
0

Aby scalić tablice w jQuery, a co z $ .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';
Rogério R. Alcântara
źródło
0

Rozwiązanie rekurencyjne (rozszerza również tablice obiektów) + zaznaczenie null

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

Testy

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 }
sscarduzio
źródło
Próbowałem użyć tego z Node JS, ale Object.getOwnPropertyNames is not a functionaplikacja uległa awarii.
Bezprawny
Używam tego z jakimś starym silnikiem v8 w rozszerzeniu plv8 PostgreSQL. I działa w przeglądarkach. Po prostu znajdź sposób w swoim silniku JS, aby zrobić to samo, co "hasOwnProperty". Następnie możesz ponownie użyć tej logiki.
sscarduzio
0

Oto najlepsze rozwiązanie.

obj1.unshift.apply( obj1, obj2 );

Ponadto, obj1mogą rosnąć wewnątrz pętli bez żadnego problemu. (powiedzmy, że obj2jest dynamiczny)

bilen
źródło