Sowohl Object.assign als auch Object Spread führen nur eine flache Zusammenführung durch.
Ein Beispiel für das Problem:
// No object nesting
const x = { a: 1 }
const y = { b: 1 }
const z = { ...x, ...y } // { a: 1, b: 1 }
Die Ausgabe entspricht Ihren Erwartungen. Wenn ich dies jedoch versuche:
// Object nesting
const x = { a: { a: 1 } }
const y = { a: { b: 1 } }
const z = { ...x, ...y } // { a: { b: 1 } }
Anstatt
{ a: { a: 1, b: 1 } }
du erhältst
{ a: { b: 1 } }
x wird vollständig überschrieben, da die Spread-Syntax nur eine Ebene tief geht. Dies ist das gleiche mit Object.assign()
.
Gibt es eine Möglichkeit, dies zu tun?
Antworten:
Nein, tut es nicht.
quelle
Ich weiß, dass dies ein altes Problem ist, aber die einfachste Lösung in ES2015 / ES6, die ich finden konnte, war mit Object.assign () recht einfach.
Hoffentlich hilft das:
Anwendungsbeispiel:
Eine unveränderliche Version davon finden Sie in der Antwort unten.
Beachten Sie, dass dies zu einer unendlichen Rekursion von Zirkelverweisen führt. Hier finden Sie einige gute Antworten zum Erkennen von Zirkelverweisen, wenn Sie glauben, dass Sie mit diesem Problem konfrontiert sind.
quelle
Object.assign(target, { [key]: {} })
wenn es einfach sein könntetarget[key] = {}
?target[key] = source[key]
stattObject.assign(target, { [key]: source[key] });
target
. Zum BeispielmergeDeep({a: 3}, {a: {b: 4}})
wird in einem Augmented führtNumber
Objekt, das eindeutig nicht erwünscht ist. AuchisObject
nicht - Arrays akzeptiert, akzeptiert aber anderen nativen Objekttyp, wie zum BeispielDate
, die nicht tief kopiert werden.Sie können Lodash Merge verwenden :
quelle
{ 'a': [{ 'b': 2 }, { 'c': 3 }, { 'd': 4 }, { 'e': 5 }] }
?{ 'a': [{ 'b': 2, 'c': 3 }, { 'd': 4, 'e': 5 }] }
ist korrekt, da wir Elemente eines Arrays zusammenführen. Das Element0
vonobject.a
ist{b: 2}
, das Element0
vonother.a
ist{c: 3}
. Wenn diese beiden zusammengeführt werden, weil sie denselben Array-Index haben, ist das Ergebnis{ 'b': 2, 'c': 3 }
das Element0
im neuen Objekt.Das Problem ist nicht trivial, wenn es um Host-Objekte oder Objekte jeglicher Art geht, die komplexer sind als eine Tüte mit Werten
Noch etwas zu beachten: Objektdiagramme, die Zyklen enthalten. Es ist normalerweise nicht schwer damit umzugehen - behalten Sie einfach ein
Set
bereits besuchtes Quellobjekt - aber oft vergessen.Sie sollten wahrscheinlich eine Deep-Merge-Funktion als Merge-Quellen schreiben, die nur primitive Werte und einfache Objekte erwartet - höchstens die Typen, die der strukturierte Klon-Algorithmus verarbeiten kann . Werfen, wenn es auf etwas stößt, das es nicht verarbeiten kann, oder nur durch Referenz zuweisen, anstatt tief zu verschmelzen.
Mit anderen Worten, es gibt keinen einheitlichen Algorithmus. Sie müssen entweder Ihren eigenen Algorithmus erstellen oder nach einer Bibliotheksmethode suchen, die Ihre Anwendungsfälle abdeckt.
quelle
Hier ist eine unveränderliche Version der Antwort von @ Salakar (ändert die Eingaben nicht). Nützlich, wenn Sie funktionale Programmieraufgaben ausführen.
quelle
key
als Eigenschaftsname, der spätere macht "Schlüssel" zum Eigenschaftsnamen. Siehe: es6-features.org/#ComputedPropertyNamesisObject
Sie nicht&& item !== null
am Ende überprüfen , weil die Zeile beginnt mititem &&
, nein?mergedDeep
der Ausgabe (glaube ich). ZBconst target = { a: 1 }; const source = { b: { c: 2 } }; const merged = mergeDeep(target, source);
merged.b.c; // 2
source.b.c = 3;
merged.b.c; // 3
Ist das ein Problem? Die Eingaben werden nicht mutiert, aber zukünftige Mutationen an den Eingaben können die Ausgabe mutieren und umgekehrt mit Mutationen, um mutierende Eingaben auszugeben. Für das, was es wert ist, hat RamdaR.merge()
das gleiche Verhalten.Da dieses Problem noch aktiv ist, ist hier ein anderer Ansatz:
quelle
prev[key] = pVal.concat(...oVal);
zuprev[key] = [...pVal, ...oVal].filter((element, index, array) => array.indexOf(element) === index);
reduce
dies nicht der Fall ist.Ich weiß, dass es bereits viele Antworten gibt und ebenso viele Kommentare, die argumentieren, dass sie nicht funktionieren werden. Der einzige Konsens ist, dass es so kompliziert ist, dass niemand einen Standard dafür gemacht hat . Die meisten akzeptierten Antworten in SO enthüllen jedoch "einfache Tricks", die weit verbreitet sind. Für uns alle wie mich, die keine Experten sind, aber sichereren Code schreiben möchten, indem wir etwas mehr über die Komplexität von Javascript erfahren, werde ich versuchen, etwas Licht ins Dunkel zu bringen.
Lassen Sie mich 2 Punkte klarstellen, bevor wir uns die Hände schmutzig machen:
Object.assign
tut.Antworten mit
for..in
oderObject.keys
sind irreführendDas Erstellen einer tiefen Kopie scheint so grundlegend und üblich zu sein, dass wir erwarten, einen Einzeiler oder zumindest einen schnellen Gewinn durch einfache Rekursion zu finden. Wir erwarten nicht, dass wir eine Bibliothek benötigen oder eine benutzerdefinierte Funktion mit 100 Zeilen schreiben sollten.
Als ich Salakars Antwort zum ersten Mal las, dachte ich wirklich, ich könnte es besser und einfacher machen (man kann es mit
Object.assign
on vergleichenx={a:1}, y={a:{b:1}}
). Dann las ich die Antwort des 8472 und dachte ... es gibt kein leichtes Entkommen , die Verbesserung bereits gegebener Antworten bringt uns nicht weit.Lassen wir einen Moment tief kopieren und rekursiv beiseite. Überlegen Sie einfach, wie (fälschlicherweise) Personen Eigenschaften analysieren, um ein sehr einfaches Objekt zu kopieren.
Object.keys
Es werden eigene nicht aufzählbare Eigenschaften, eigene symbolgesteuerte Eigenschaften und alle Eigenschaften des Prototyps weggelassen. Es kann in Ordnung sein, wenn Ihre Objekte keine davon haben. Beachten Sie jedoch, dassObject.assign
die eigenen aufzählbaren Eigenschaften mit Symbolschlüssel behandelt werden. So hat Ihre benutzerdefinierte Kopie ihre Blüte verloren.for..in
liefert Eigenschaften der Quelle, ihres Prototyps und der gesamten Prototypenkette, ohne dass Sie dies wünschen (oder wissen). Ihr Ziel hat möglicherweise zu viele Eigenschaften, wodurch Prototyp-Eigenschaften und eigene Eigenschaften verwechselt werden.Wenn Sie eine Mehrzweckfunktion zu schreiben und Sie nicht verwenden
Object.getOwnPropertyDescriptors
,Object.getOwnPropertyNames
,Object.getOwnPropertySymbols
oderObject.getPrototypeOf
sind Sie wahrscheinlich etwas falsch gemacht.Dinge zu beachten, bevor Sie Ihre Funktion schreiben
Stellen Sie zunächst sicher, dass Sie verstehen, was ein Javascript-Objekt ist. In Javascript besteht ein Objekt aus eigenen Eigenschaften und einem (übergeordneten) Prototypobjekt. Das Prototypobjekt wiederum besteht aus eigenen Eigenschaften und einem Prototypobjekt. Und so weiter, Definition einer Prototypkette.
Eine Eigenschaft ist ein Paar aus Schlüssel (
string
odersymbol
) und Deskriptor (value
oderget
/set
Accessor und Attributen wieenumerable
).Schließlich gibt es viele Arten von Objekten . Möglicherweise möchten Sie ein Objekt Objekt anders behandeln als ein Objekt Datum oder eine Objektfunktion.
Wenn Sie also Ihre tiefe Kopie schreiben, sollten Sie mindestens die folgenden Fragen beantworten:
In meinem Beispiel bin ich der Meinung, dass nur die
object Object
s tief sind , da andere Objekte, die von anderen Konstruktoren erstellt wurden, möglicherweise nicht für eine eingehende Betrachtung geeignet sind. Angepasst von diesem SO .Und ich habe ein
options
Objekt erstellt, um auszuwählen, was kopiert werden soll (für Demozwecke).Vorgeschlagene Funktion
Sie können es in diesem Plunker testen .
Das kann so verwendet werden:
quelle
Ich benutze lodash:
quelle
_cloneDeep(value1).merge(value2)
Hier ist die TypeScript-Implementierung:
Und Unit Tests:
quelle
Das deepmerge npm-Paket scheint die am häufigsten verwendete Bibliothek zur Lösung dieses Problems zu sein: https://www.npmjs.com/package/deepmerge
quelle
Ich möchte eine ziemlich einfache ES5-Alternative vorstellen. Die Funktion erhält 2 Parameter -
target
undsource
das muss vom Typ "Objekt" sein.Target
wird das resultierende Objekt sein.Target
behält alle ursprünglichen Eigenschaften bei, aber ihre Werte können geändert werden.Fälle:
target
keinesource
Eigenschaft haben, erhaltentarget
Sie diese.target
einesource
Eigenschaft vorhanden ist undtarget
&source
nicht beide Objekte sind (3 von 4 Fällen), wirdtarget
die Eigenschaft überschrieben.target
einesource
Eigenschaft vorhanden ist und beide Objekte / Arrays sind (1 verbleibender Fall), erfolgt eine Rekursion, bei der zwei Objekte zusammengeführt werden (oder zwei Arrays verkettet werden).Beachten Sie auch Folgendes :
Es ist vorhersehbar, unterstützt primitive Typen sowie Arrays und Objekte. Da wir auch 2 Objekte zusammenführen können, denke ich, dass wir über die Reduktionsfunktion mehr als 2 zusammenführen können.
Schauen Sie sich ein Beispiel an (und spielen Sie damit herum, wenn Sie möchten) :
Es gibt eine Einschränkung - die Länge des Aufrufstapels des Browsers. Moderne Browser geben einen Fehler auf einer wirklich tiefen Rekursionsstufe aus (denken Sie an Tausende verschachtelter Aufrufe). Sie können auch Situationen wie Array + Objekt usw. nach Belieben behandeln, indem Sie neue Bedingungen und Typprüfungen hinzufügen.
quelle
Hier ist eine weitere ES6-Lösung, die mit Objekten und Arrays arbeitet.
quelle
Wenn Sie ImmutableJS verwenden , können Sie Folgendes verwenden
mergeDeep
:quelle
Wenn npm-Bibliotheken als Lösung verwendet werden können, können Sie mit Object-Merge-Advanced von Ihnen Objekte wirklich gründlich zusammenführen und jede einzelne Zusammenführungsaktion mithilfe einer bekannten Rückruffunktion anpassen / überschreiben. Die Hauptidee ist es mehr als nur tiefe Verschmelzung - was mit dem Wert passiert , wenn zwei Tasten sind die gleichen ? Diese Bibliothek kümmert sich darum - wenn zwei Schlüssel zusammenstoßen, werden
object-merge-advanced
die Typen gewogen, um nach dem Zusammenführen so viele Daten wie möglich zu erhalten:Der Schlüssel des ersten Eingabearguments ist mit # 1 markiert, der des zweiten Arguments mit # 2. Abhängig von jedem Typ wird einer für den Wert des Ergebnisschlüssels ausgewählt. Im Diagramm bedeutet "ein Objekt" ein einfaches Objekt (kein Array usw.).
Wenn die Tasten nicht zusammenstoßen, geben sie alle das Ergebnis ein.
Wenn Sie aus Ihrem Beispiel-Snippet
object-merge-advanced
Ihr Code-Snippet zusammengeführt haben:Der Algorithmus durchläuft rekursiv alle Eingabeobjektschlüssel, vergleicht und erstellt das neue zusammengeführte Ergebnis und gibt es zurück.
quelle
Die folgende Funktion erstellt eine tiefe Kopie von Objekten. Sie umfasst das Kopieren von Grundelementen, Arrays und Objekten
quelle
Eine einfache Lösung mit ES5 (vorhandenen Wert überschreiben):
quelle
Die meisten Beispiele hier scheinen zu komplex zu sein. Ich verwende eines in TypeScript, das ich erstellt habe. Ich denke, es sollte die meisten Fälle abdecken (ich behandle Arrays als reguläre Daten und ersetze sie nur).
Das Gleiche in einfachem JS, nur für den Fall:
Hier sind meine Testfälle, um zu zeigen, wie Sie es verwenden können
Bitte lassen Sie mich wissen, wenn Sie glauben, dass mir einige Funktionen fehlen.
quelle
Wenn Sie einen Einzeiler haben möchten, ohne eine große Bibliothek wie lodash zu benötigen, empfehle ich Ihnen, deepmerge zu verwenden . (
npm install deepmerge
)Dann können Sie tun
bekommen
Das Schöne ist, dass es sofort mit Typing für TypeScript kommt. Es ermöglicht auch das Zusammenführen von Arrays . Dies ist eine echte Allrounder-Lösung.
quelle
Wir können $ .extend (true, object1, object2) für die Tiefenverschmelzung verwenden. Der Wert true bedeutet, dass zwei Objekte rekursiv zusammengeführt werden, wobei das erste geändert wird.
$ verlängern (wahr, Ziel, Objekt)
quelle
jQuery.isPlainObject()
. Dies zeigt die Komplexität der Bestimmung, ob etwas ein einfaches Objekt ist oder nicht, was die meisten Antworten hier bei weitem übersehen. Ratet mal, in welcher Sprache jQuery geschrieben ist?Hier eine einfache Lösung, die wie ein
Object.assign
Deeep funktioniert und für ein Array ohne Änderungen funktioniertBeispiel
quelle
Ich hatte dieses Problem beim Laden eines zwischengespeicherten Redux-Status. Wenn ich nur den zwischengespeicherten Status lade, treten bei der neuen App-Version mit einer aktualisierten Statusstruktur Fehler auf.
Es wurde bereits erwähnt, dass lodash die
merge
Funktion bietet , die ich verwendet habe:quelle
Viele Antworten verwenden mehrere zehn Codezeilen oder erfordern das Hinzufügen einer neuen Bibliothek zum Projekt. Wenn Sie jedoch die Rekursion verwenden, sind dies nur vier Codezeilen.
Arrays-Behandlung: Die obige Version überschreibt alte Array-Werte mit neuen. Wenn Sie möchten, dass die alten Array-Werte beibehalten und die neuen hinzugefügt werden, fügen Sie einfach einen
else if (current[key] instanceof Array && updates[key] instanceof Array) current[key] = current[key].concat(updates[key])
Block über demelse
Statuen hinzu, und schon sind Sie fertig.quelle
Hier ist eine andere, die ich gerade geschrieben habe und die Arrays unterstützt. Es konzentriert sie.
quelle
Verwenden Sie diese Funktion:
quelle
Ramda, eine schöne Bibliothek von Javascript-Funktionen, hat mergeDeepLeft und mergeDeepRight. Alle diese funktionieren ziemlich gut für dieses Problem. Bitte sehen Sie sich die Dokumentation hier an: https://ramdajs.com/docs/#mergeDeepLeft
Für das spezifische Beispiel können wir verwenden:
quelle
Gerätetest:
quelle
Ich habe nur eine zweizeilige Lösung gefunden, um eine tiefe Zusammenführung in Javascript zu erreichen. Lassen Sie mich wissen, wie das für Sie funktioniert.
Das temporäre Objekt druckt {a: {b: 'd', e: 'f', x: 'y'}}
quelle
merge({x:{y:{z:1}}}, {x:{y:{w:2}}})
. Il kann auch vorhandene Werte in obj1 nicht aktualisieren, wenn obj2 sie auch hat, zum Beispiel mitmerge({x:{y:1}}, {x:{y:2}})
.Manchmal brauchen Sie keine tiefe Verschmelzung, selbst wenn Sie so denken. Wenn Sie beispielsweise eine Standardkonfiguration mit verschachtelten Objekten haben und diese mit Ihrer eigenen Konfiguration erweitern möchten, können Sie eine Klasse dafür erstellen. Das Konzept ist sehr einfach:
Sie können es in eine Funktion konvertieren (keinen Konstruktor).
quelle
Dies ist eine billige Deep Merge, die so wenig Code verwendet, wie ich mir vorstellen kann. Jede Quelle überschreibt die vorherige Eigenschaft, wenn sie vorhanden ist.
quelle
Ich verwende die folgende kurze Funktion zum tiefen Zusammenführen von Objekten.
Es funktioniert großartig für mich.
Der Autor erklärt hier vollständig, wie es funktioniert.
quelle