Gegeben ein JS-Objekt
var obj = { a: { b: '1', c: '2' } }
und eine Schnur
"a.b"
Wie kann ich die Zeichenfolge in Punktnotation konvertieren, damit ich gehen kann?
var val = obj.a.b
Wenn die Zeichenfolge nur wäre 'a'
, könnte ich verwenden obj[a]
. Das ist aber komplexer. Ich stelle mir vor, dass es eine einfache Methode gibt, die aber derzeit entgeht.
javascript
nevf
quelle
quelle
eval
ist böse; benutze es nichtAntworten:
Hier ist ein eleganter Einzeiler, der 10x kürzer ist als die anderen Lösungen:
[Bearbeiten] Oder in ECMAScript 6:
(Nicht, dass ich denke, dass eval immer schlecht ist, wie andere vermuten, dass es ist (obwohl es normalerweise so ist), dennoch werden sich diese Leute freuen, dass diese Methode eval nicht verwendet. Die oben genannten finden
obj.a.b.etc
gegebenobj
und die Zeichenfolge"a.b.etc"
.)Als Antwort auf diejenigen, die immer noch Angst vor der Verwendung haben,
reduce
obwohl sie im ECMA-262-Standard (5. Ausgabe) enthalten sind, gibt es hier eine zweizeilige rekursive Implementierung:Abhängig von den Optimierungen, die der JS-Compiler vornimmt, möchten Sie möglicherweise sicherstellen, dass verschachtelte Funktionen nicht bei jedem Aufruf über die üblichen Methoden neu definiert werden (Platzieren in einem Abschluss, Objekt oder globalen Namespace).
bearbeiten :
Um eine interessante Frage in den Kommentaren zu beantworten:
(Nebenbemerkung: Leider kann ein Objekt nicht mit einem Setter zurückgegeben werden, da dies gegen die aufrufende Konvention verstoßen würde. Der Kommentator scheint sich stattdessen auf eine allgemeine Setter-Funktion mit Nebenwirkungen wie "
index(obj,"a.b.etc", value)
Doing" zu beziehenobj.a.b.etc = value
.)Der
reduce
Stil ist dafür nicht wirklich geeignet, aber wir können die rekursive Implementierung ändern:Demo:
... obwohl ich persönlich empfehlen würde, eine separate Funktion zu erstellen
setIndex(...)
. Ich möchte mit einer Randnotiz enden, dass der ursprüngliche Poser der Frage eher mit Arrays von Indizes (von denen sie stammen können.split
) als mit Strings arbeiten könnte (sollte?) ; obwohl an einer Komfortfunktion normalerweise nichts auszusetzen ist.Ein Kommentator fragte:
Javascript ist eine sehr seltsame Sprache; Im Allgemeinen können Objekte nur Zeichenfolgen als Eigenschaftsschlüssel haben. Wenn
x
es sich beispielsweise um ein generisches Objekt handeltx={}
, wirdx[1]
dies zux["1"]
... Sie lesen das richtig ... yup ...Javascript-Arrays (die selbst Instanzen von Object sind) fördern speziell Ganzzahlschlüssel, obwohl Sie so etwas tun könnten
x=[]; x["puppy"]=5;
.Aber im Allgemeinen (und es gibt Ausnahmen)
x["somestring"]===x.somestring
(wenn es erlaubt ist; können Sie nichtx.123
).(Denken Sie daran, dass jeder JS-Compiler, den Sie verwenden, diese möglicherweise zu vernünftigeren Darstellungen kompilieren kann, wenn dies beweisen kann, dass sie nicht gegen die Spezifikation verstoßen.)
Die Antwort auf Ihre Frage hängt also davon ab, ob Sie davon ausgehen, dass diese Objekte nur Ganzzahlen akzeptieren (aufgrund einer Einschränkung in Ihrer Problemdomäne) oder nicht. Nehmen wir nicht an. Dann ist ein gültiger Ausdruck eine Verkettung einer Basiskennung plus einiger
.identifier
s plus einiger["stringindex"]
sDies wäre dann gleichbedeutend mit
a["b"][4]["c"]["d"][1][2][3]
, obwohl wir wahrscheinlich auch unterstützen solltena.b["c\"validjsstringliteral"][3]
. Sie müssten den Abschnitt zur Ecmascript-Grammatik für Zeichenfolgenliterale überprüfen, um zu sehen, wie ein gültiges Zeichenfolgenliteral analysiert wird. Technisch gesehen möchten Sie auch (anders als in meiner ersten Antwort) überprüfen, oba
es sich um eine gültige Javascript-Kennung handelt .Eine einfache Antwort auf Ihre Frage aber, wenn die Saiten Kommas oder Klammern nicht enthalten , würde sein gerade Länge 1+ Sequenzen von Zeichen in der Menge nicht übereinstimmen
,
oder[
oder]
:Wenn Ihre Zeichenfolgen keine Escape-Zeichen oder
"
Zeichen enthalten und IdentifierNames eine Subsprache von StringLiterals sind (glaube ich ???), können Sie Ihre Punkte zuerst in [] konvertieren:Seien Sie natürlich immer vorsichtig und vertrauen Sie niemals Ihren Daten. Einige schlechte Methoden, die für einige Anwendungsfälle funktionieren könnten, umfassen auch:
Special 2018 bearbeiten:
Lassen Sie uns den Kreis schließen und die ineffizienteste, schrecklich übermetaprogrammierte Lösung finden, die wir finden können ... im Interesse der Hamfisterie mit syntaktischer
Reinheit. Mit ES6-Proxy-Objekten! ... Definieren wir auch einige Eigenschaften, die (imho sind in Ordnung und wunderbar, aber) möglicherweise falsch geschriebene Bibliotheken beschädigen. Sie sollten vielleicht vorsichtig sein, wenn Sie sich um Leistung, geistige Gesundheit (Ihre oder die anderer), Ihren Job usw. kümmern.Demo:
Ausgabe:
ineffiziente Idee: Sie können das oben Gesagte basierend auf dem Eingabeargument so ändern, dass es versendet. Verwenden Sie entweder die
.match(/[^\]\[.]+/g)
Methode zur Unterstützungobj['keys'].like[3]['this']
oder wenninstanceof Array
, dann akzeptieren Sie einfach ein Array als Eingabe wiekeys = ['a','b','c']; obj.H[keys]
.Per Vorschlag, dass Sie möglicherweise undefinierte Indizes in einem "weicheren" NaN-Stil behandeln möchten (z. B.
index({a:{b:{c:...}}}, 'a.x.c')
undefinierte anstelle von nicht erfassten TypeError zurückgeben) ...:1) Dies ist unter dem Gesichtspunkt "Wir sollten undefiniert zurückgeben, anstatt einen Fehler zu werfen" in der eindimensionalen Indexsituation ({}) ['eg'] == undefiniert sinnvoll. "Wir sollten also undefiniert zurückgeben, anstatt einen Fehler zu werfen." Fehler "in der N-dimensionalen Situation.
2) Dies ist aus der Perspektive, die wir machen , nicht sinnvoll
x['a']['x']['c']
, was mit einem TypeError im obigen Beispiel fehlschlagen würde.Das heißt, Sie würden diese Arbeit machen, indem Sie Ihre Reduktionsfunktion durch Folgendes ersetzen:
(o,i)=>o===undefined?undefined:o[i]
oder(o,i)=>(o||{})[i]
.(Sie können dies effizienter gestalten, indem Sie eine for-Schleife verwenden und abbrechen / zurückgeben, wenn das Unterergebnis, in das Sie als nächstes indizieren möchten, nicht definiert ist, oder einen Try-Catch verwenden, wenn Sie erwarten, dass solche Fehler ausreichend selten sind.)
quelle
reduce
wird nicht in allen derzeit verwendeten Browsern unterstützt.Array.reduce
ist Teil des ECMA-262-Standards. Wenn Sie wirklich veraltete Browser unterstützen möchten, können SieArray.prototype.reduce
die irgendwo angegebene Beispielimplementierung definieren (z . B. developer.mozilla.org/en/JavaScript/Reference/Global_Objects/… ).var setget = function( obj, path ){ function index( robj,i ) {return robj[i]}; return path.split('.').reduce( index, obj ); }
Wenn Sie lodash verwenden können , gibt es eine Funktion, die genau das tut:
_.get (Objekt, Pfad, [Standardwert])
quelle
_.get(object, path)
Wird nicht unterbrochen, wenn kein Pfad gefunden wurde.'a.b.etc'.split('.').reduce((o,i)=>o[i], obj)
tut. Für meinen speziellen Fall - nicht für jeden Fall - genau das, was ich brauchte. Vielen Dank!defaultValue
. Die_.get()
Methode gibt den Standardwert zurück, wenn er_.get()
in aufgelöstundefined
wird. Setzen Sie ihn also auf den gewünschten Wert und achten Sie auf den von Ihnen festgelegten Wert._.set(object, path, value)
.Ein etwas komplizierteres Beispiel mit Rekursion.
quelle
Sie können auch lodash.get verwenden
Sie installieren einfach dieses Paket (npm i --save lodash.get) und verwenden es dann folgendermaßen:
quelle
Wenn Sie davon ausgehen, dass Sie denselben Pfad mehrmals dereferenzieren, hat das Erstellen einer Funktion für jeden Punktnotationspfad bei weitem die beste Leistung (Erweiterung der Perf-Tests, auf die James Wilkins in den obigen Kommentaren verwiesen hat).
Die Verwendung des Funktionskonstruktors hat einige der gleichen Nachteile wie eval () in Bezug auf Sicherheit und Worst-Case-Leistung, aber IMO ist ein schlecht genutztes Tool für Fälle, in denen Sie eine Kombination aus extremer Dynamik und hoher Leistung benötigen. Ich verwende diese Methode, um Array-Filterfunktionen zu erstellen und sie in einer AngularJS-Digest-Schleife aufzurufen. Meine Profile zeigen konsistent den Schritt array.filter (), der weniger als 1 ms benötigt, um etwa 2000 komplexe Objekte zu dereferenzieren und zu filtern, wobei dynamisch definierte Pfade mit einer Tiefe von 3-4 Ebenen verwendet werden.
Eine ähnliche Methodik könnte natürlich verwendet werden, um Setterfunktionen zu erstellen:
quelle
Viele Jahre seit dem ursprünglichen Beitrag. Jetzt gibt es eine großartige Bibliothek namens 'Objektpfad'. https://github.com/mariocasciaro/object-path
Verfügbar auf NPM und BOWER https://www.npmjs.com/package/object-path
Es ist so einfach wie:
Und funktioniert für tief verschachtelte Eigenschaften und Arrays.
quelle
Ich schlage vor, den Pfad zu teilen und zu iterieren und das Objekt zu reduzieren, das Sie haben. Dieser Vorschlag arbeitet mit einem Standardwert für fehlende Eigenschaften.
quelle
Hinweis: Wenn Sie Lodash bereits verwenden, können Sie die Funktionen
property
oderget
verwenden:Unterstrich hat auch eine
property
Funktion, unterstützt jedoch keine Punktnotation.quelle
Andere Vorschläge sind etwas kryptisch, daher dachte ich, ich würde dazu beitragen:
quelle
Sie können die unter npm verfügbare Bibliothek verwenden, was diesen Vorgang vereinfacht. https://www.npmjs.com/package/dot-object
quelle
Dies ist eine Menge Code im Vergleich zu der viel einfacheren
eval
Methode, aber wie Simon Willison sagt, sollten Sie niemals eval verwenden .Auch JSFiddle .
quelle
Ich habe die elegante Antwort um ninjagecko erweitert, sodass die Funktion sowohl gepunktete als auch / oder Array-Stilreferenzen verarbeitet und eine leere Zeichenfolge bewirkt, dass das übergeordnete Objekt zurückgegeben wird.
Bitte schön:
Siehe mein funktionierendes jsFiddle-Beispiel hier: http://jsfiddle.net/sc0ttyd/q7zyd/
quelle
[]
ausgegangen , dass die Notation immer für Arrays gilt. Auf diese Weise können auch Objektschlüssel dargestellt werden.obj['some-problem/name'].list[1]
Um dies zu beheben, musste ich diearr_deref
Funktion wiejavascript function arr_deref(o, ref, i) { return !ref ? o : (o[(ref.slice(0, i ? -1 : ref.length)).replace(/^['"]|['"]$/g, '')]); }
quelle
Sie können den Wert eines Objektelements durch Punktnotation mit einer einzelnen Codezeile erhalten:
In Ihrem Fall:
Zur Vereinfachung können Sie eine Funktion wie die folgende schreiben:
Erläuterung:
Der Funktionskonstruktor erstellt ein neues Funktionsobjekt. In JavaScript ist jede Funktion tatsächlich ein Funktionsobjekt. Die Syntax zum expliziten Erstellen einer Funktion mit dem Funktionskonstruktor lautet:
Dabei
arguments(arg1 to argN)
muss es sich um eine Zeichenfolge handeln, die einem gültigen JavaScript-Bezeichner und entsprichtfunctionBody
eine Zeichenfolge enthält, die die JavaScript-Anweisungen enthält, aus denen die Funktionsdefinition besteht.In unserem Fall nutzen wir den Vorteil des String-Funktionskörpers, um ein Objektelement mit Punktnotation abzurufen.
Ich hoffe es hilft.
quelle
GET / SET- Antwort, die auch in React Native funktioniert (die Sie derzeit nicht zuweisen können
Object.prototype
):Verwendung:
quelle
Ich habe das Folgende aus Ricardo Tomasis Antwort kopiert und modifiziert, um auch Unterobjekte zu erstellen, die bei Bedarf noch nicht existieren. Es ist etwas weniger effizient (mehr
if
s und das Erstellen leerer Objekte), sollte aber ziemlich gut sein.Außerdem können wir dort tun,
Object.prop(obj, 'a.b', false)
wo wir vorher nicht konnten. Leider lässt es uns immer noch nicht zuweisenundefined
... Ich bin mir noch nicht sicher, wie ich das anstellen soll.quelle
Wenn Sie ein Objekt, das Punktnotationsschlüssel enthält, in eine Array-Version dieser Schlüssel konvertieren möchten, können Sie dies verwenden.
Dies wird so etwas wie konvertieren
zu
quelle
Hier ist mein Code ohne Verwendung
eval
. Es ist auch leicht zu verstehen.quelle
Ja, es wurde vor 4 Jahren gefragt, und ja, das Erweitern von Basisprototypen ist normalerweise keine gute Idee, aber wenn Sie alle Erweiterungen an einem Ort aufbewahren, können sie nützlich sein.
Also, hier ist mein Weg, dies zu tun.
Jetzt können Sie überall verschachtelte Eigenschaften abrufen, ohne ein Modul mit Funktion oder Kopier- / Einfügefunktion zu importieren.
UPD.Beispiel:
UPD 2. Die nächste Erweiterung bricht Mungo in meinem Projekt. Ich habe auch gelesen, dass es jquery kaputt machen könnte. Also mach es niemals auf die nächste Weise
quelle
Hier ist meine Implementierung
Implementierung 1
Implementierung 2 (mit Array Reduce anstelle von Slice)
Beispiele:
Es kann auch Objekte in Arrays wie für behandeln
quelle
Dies ist meine erweiterte Lösung, vorgeschlagen von: ninjagecko
Für mich war eine einfache String-Notation nicht genug, daher unterstützt die folgende Version Dinge wie:
index (obj, 'data.accounts [0] .address [0] .postcode');
quelle
Auf die Gefahr hin, ein totes Pferd zu schlagen ... Ich finde dies am nützlichsten, wenn ich verschachtelte Objekte durchquere, um zu verweisen, wo Sie sich in Bezug auf das Basisobjekt oder ein ähnliches Objekt mit derselben Struktur befinden. Zu diesem Zweck ist dies bei einer Durchlauffunktion für verschachtelte Objekte nützlich. Beachten Sie, dass ich ein Array verwendet habe, um den Pfad zu halten. Es wäre trivial, dies so zu ändern, dass entweder ein Zeichenfolgenpfad oder ein Array verwendet wird. Beachten Sie auch, dass Sie dem Wert im Gegensatz zu einigen anderen Implementierungen "undefiniert" zuweisen können.
quelle
Ich habe diesen Code in meinem Projekt verwendet
Verwendung:
quelle
Einige Jahre später fand ich dies, das Umfang und Array behandelt. z.B
a['b']["c"].d.etc
quelle
Es ist nicht klar, was Ihre Frage ist. Wenn Sie Ihr Objekt erhalten, erhalten
obj.a.b
Sie "2", so wie es ist. Wenn Sie die Zeichenfolge so manipulieren möchten, dass Klammern verwendet werden, können Sie Folgendes tun:quelle
a.b.c
und erreicht nicht wirklich das, was sie wollen. Sie wollen den Wert, keineneval
Pfad.a.b.c
, aber Sie haben Recht, anscheinend wollte er den Wert der Eigenschaft auf / setzenobj.a.b
. Die Frage war für mich verwirrend, da er sagte, er wolle "die Saite konvertieren" ...Hier sind meine 10 Cent für den Fall, die folgende Funktion wird basierend auf dem angegebenen Pfad abgerufen / eingestellt. Sicher, dass Sie sie verbessern können, entfernen || und ersetzen Sie es durch,
Object.hasOwnProperty
wenn Sie sich versehentlich um falsche Werte kümmern.Ich habe es mit
a.b.c
und ab2.c getestet{a:{b:[0,1,{c:7}]}}
funktioniert sowohl zum Setzen als auch zum Erhalten :).cheerz
quelle