688 Stimmen

Zugriff auf verschachtelte JavaScript-Objekte und -Arrays über einen String-Pfad

Ich habe eine Datenstruktur wie diese:

var someObject = {
    'part1' : {
        'name': 'Part 1',
        'size': '20',
        'qty' : '50'
    },
    'part2' : {
        'name': 'Part 2',
        'size': '15',
        'qty' : '60'
    },
    'part3' : [
        {
            'name': 'Part 3A',
            'size': '10',
            'qty' : '20'
        }, {
            'name': 'Part 3B',
            'size': '5',
            'qty' : '20'
        }, {
            'name': 'Part 3C',
            'size': '7.5',
            'qty' : '20'
        }
    ]
};

Und ich möchte mit diesen Variablen auf die Daten zugreifen:

var part1name = "part1.name";
var part2quantity = "part2.qty";
var part3name1 = "part3[0].name";

part1name sollte gefüllt werden mit someObject.part1.name Wert, der "Teil 1" ist. Dasselbe gilt für part2quantity, das mit 60 gefüllt ist.

Gibt es irgendwie zu erreichen dies entweder mit reinen Javascript oder JQuery?

0 Stimmen

Sie sind sich nicht sicher, was Sie hier fragen? Möchten Sie part1.name abfragen können und den Text "part1.name" zurückerhalten? Oder möchten Sie den in part1.name gespeicherten Wert abrufen?

0 Stimmen

Haben Sie versucht, etwas zu tun wie var part1name = someObject.part1name; `

1 Stimmen

@BonyT : Ich möchte someObject.part1.name abfragen und den Wert davon zurückgeben ("Part 1"). Ich möchte jedoch, dass die Abfrage (ich nannte sie "den Schlüssel") in einer Variablen 'part1name' gespeichert wird. Vielen Dank für Ihre Antwort. @3nigma : Das habe ich sicherlich. Aber das ist nicht meine Absicht. Vielen Dank für die Antwort.

681voto

Alnitak Punkte 324207

Ich habe dies auf der Grundlage eines ähnlichen Codes gemacht, den ich bereits hatte, und es scheint zu funktionieren:

Object.byString = function(o, s) {
    s = s.replace(/\[(\w+)\]/g, '.$1'); // convert indexes to properties
    s = s.replace(/^\./, '');           // strip a leading dot
    var a = s.split('.');
    for (var i = 0, n = a.length; i < n; ++i) {
        var k = a[i];
        if (k in o) {
            o = o[k];
        } else {
            return;
        }
    }
    return o;
}

Verwendung::

Object.byString(someObj, 'part3[0].name');

Sehen Sie eine funktionierende Demo unter http://jsfiddle.net/alnitak/hEsys/

EDIT einige haben bemerkt, dass dieser Code einen Fehler auslöst, wenn eine Zeichenkette übergeben wird, bei der die äußersten linken Indizes nicht einem korrekt verschachtelten Eintrag innerhalb des Objekts entsprechen. Dies ist ein berechtigtes Anliegen, das aber IMHO am besten mit einer try / catch beim Aufruf blockieren, anstatt diese Funktion stillschweigend zurückkehren zu lassen undefined für einen ungültigen Index.

0 Stimmen

Ihre Antwort ist genau das, was ich brauche. Sie funktioniert wie ein Zauber. Felix Kling hat jedoch einen anderen Weg gefunden, der genauso gut funktioniert wie Ihre Antwort. Ich habe einfach das Gefühl, dass ich seine Antwort auch als die richtige erwähnen muss. Wie auch immer, vielen Dank für die Lösung, die Sie mir gegeben haben.

0 Stimmen

Was ist, wenn der Wert Null ist? Wie kann ich damit umgehen?

42 Stimmen

Das funktioniert wunderbar. Bitte stellen Sie dies dem Internet zur Verfügung, indem Sie es als Node-Paket verpacken.

306voto

Ian Walker-Sperber Punkte 3321

Dies wird nun von lodash unterstützt durch _.get(obj, property) . Siehe https://lodash.com/docs#get

Beispiel aus der Dokumentation:

var object = { 'a': [{ 'b': { 'c': 3 } }] };

_.get(object, 'a[0].b.c');
//  3

_.get(object, ['a', '0', 'b', 'c']);
//  3

_.get(object, 'a.b.c', 'default');
//  'default'

12 Stimmen

Dies sollte die einzige akzeptierte Antwort sein, da dies die einzige ist, die sowohl für die Punkt- als auch für die Klammersyntax funktioniert und nicht fehlschlägt, wenn wir '[]' in der Zeichenfolge eines Schlüssels im Pfad haben.

12 Stimmen

Dies. Außerdem unterstützt es _.set(...)

0 Stimmen

Was geschieht, wenn das Objekt nicht gefunden wird?

273voto

speigg Punkte 2480

Dies ist die Lösung, die ich verwende:

function resolve(path, obj=self, separator='.') {
    var properties = Array.isArray(path) ? path : path.split(separator)
    return properties.reduce((prev, curr) => prev?.[curr], obj)
}

Beispiel für die Verwendung:

// accessing property path on global scope
resolve("document.body.style.width")
// or
resolve("style.width", document.body)

// accessing array indexes
// (someObject has been defined in the question)
resolve("part3.0.size", someObject) // returns '10'

// accessing non-existent properties
// returns undefined when intermediate properties are not defined:
resolve('properties.that.do.not.exist', {hello:'world'})

// accessing properties with unusual keys by changing the separator
var obj = { object: { 'a.property.name.with.periods': 42 } }
resolve('object->a.property.name.with.periods', obj, '->') // returns 42

// accessing properties with unusual keys by passing a property name array
resolve(['object', 'a.property.name.with.periods'], obj) // returns 42

Beschränkungen:

  • Klammern können nicht verwendet werden ( [] ) für Array-Indizes - obwohl die Angabe von Array-Indizes zwischen dem Trennzeichen (z. B., . ) funktioniert wie oben gezeigt einwandfrei.

7 Stimmen

Die Verwendung von reduce ist eine ausgezeichnete Lösung (man kann auch _.reduce() aus der underscore- oder lodash-Bibliothek)

5 Stimmen

Ich denke self ist hier wahrscheinlich undefiniert. Meinen Sie this ?

0 Stimmen

Lodash + Typescript-Implementierung: _.reduce(path.split('.'), (previous, current) => !safe ? previous[current] : (previous ? previous[current] : undefined), target || self);

255voto

Adriano Spadoni Punkte 3967

ES6 : Nur eine Zeile in Vanila JS (sie gibt null zurück, wenn sie nicht gefunden wird, anstatt einen Fehler zu melden):

'path.string'.split('.').reduce((p,c)=>p&&p[c]||null, MyOBJ)

Oder ein Beispiel:

'a.b.c'.split('.').reduce((p,c)=>p&&p[c]||null, {a:{b:{c:1}}})

Mit optionalem Verkettungsoperator :

'a.b.c'.split('.').reduce((p,c)=>p?.[c], {a:{b:{c:1}}})

Für eine einsatzbereite Funktion, die auch false, 0 und negative Zahlen erkennt und Standardwerte als Parameter akzeptiert:

const resolvePath = (object, path, defaultValue) => path
   .split('.')
   .reduce((o, p) => o ? o[p] : defaultValue, object)

Beispiel zur Verwendung:

resolvePath(window,'document.body') => <body>
resolvePath(window,'document.body.xyz') => undefined
resolvePath(window,'document.body.xyz', null) => null
resolvePath(window,'document.body.xyz', 1) => 1

Bonus :

An einstellen. einen Pfad (erbeten von @rob-gordon), den Sie verwenden können:

const setPath = (object, path, value) => path
   .split('.')
   .reduce((o,p,i) => o[p] = path.split('.').length === ++i ? value : o[p] || {}, object)

Beispiel:

let myVar = {}
setPath(myVar, 'a.b.c', 42) => 42
console.log(myVar) => {a: {b: {c: 42}}}

Zugriff auf Array mit [] :

const resolvePath = (object, path, defaultValue) => path
   .split(/[\.\[\]\'\"]/)
   .filter(p => p)
   .reduce((o, p) => o ? o[p] : defaultValue, object)

Beispiel:

const myVar = {a:{b:[{c:1}]}}
resolvePath(myVar,'a.b[0].c') => 1
resolvePath(myVar,'a["b"][\'0\'].c') => 1

3 Stimmen

Ich liebe diese Technik. Das ist wirklich unordentlich, aber ich wollte diese Technik für eine Aufgabe verwenden. let o = {a:{b:{c:1}}}; let str = 'a.b.c'; str.split('.').splice(0, str.split('.').length - 1).reduce((p,c)=>p&&p[c]||null, o)[str.split('.').slice(-1)] = "some new value";

5 Stimmen

Ich mag die Idee, reduce zu verwenden, aber Ihre Logik scheint nicht für 0 , undefined et null Werte. {a:{b:{c:0}}} gibt zurück. null 代わりに 0 . Vielleicht kann die explizite Prüfung auf null oder undefiniert diese Probleme beheben. (p,c)=>p === undefined || p === null ? undefined : p[c]

1 Stimmen

Hallo @SmujMaiku, die "gebrauchsfertige" Funktion gibt korrekt für '0', 'undefiniert' und 'null' zurück, ich habe gerade auf der Konsole getestet: resolvePath({a:{b:{c:0}}},'a.b.c',null) => 0; Es wird geprüft, ob der Schlüssel existiert, anstatt des Wertes selbst, was mehr als eine Prüfung vermeidet

67voto

Felix Kling Punkte 751464

Sie müssen die Zeichenfolge selbst analysieren:

function getProperty(obj, prop) {
    var parts = prop.split('.');

    if (Array.isArray(parts)) {
        var last = parts.pop(),
        l = parts.length,
        i = 1,
        current = parts[0];

        while((obj = obj[current]) && i < l) {
            current = parts[i];
            i++;
        }

        if(obj) {
            return obj[last];
        }
    } else {
        throw 'parts is not valid array';
    }
}

Dies erforderte, dass Sie auch Array-Indizes in Punktschreibweise definieren:

var part3name1 = "part3.0.name";

Das macht das Parsing einfacher.

DEMO

0 Stimmen

@Felix Kling : Ihre Lösung bietet mir, was ich brauche. Und ich danke Ihnen sehr dafür. Aber Alnitak bietet auch andere Möglichkeiten und scheint auch zu funktionieren. Da ich nur eine Antwort wählen kann, werde ich mich für Alnitaks Lösung entscheiden. Nicht, dass seine Lösung besser ist als Ihre oder so etwas. Wie auch immer, ich schätze Ihre Lösung und die Mühe, die Sie sich gemacht haben, sehr.

0 Stimmen

@Felix FWIW - Konvertierung von [] Syntax zur Eigenschaftssyntax ist ziemlich trivial.

0 Stimmen

Mir gefällt diese Antwort, weil ich meinen Benutzern ein einfacheres Format für die Pfade geben kann - ich verwende die Punktschreibweise für Indizes anstelle von Klammern. Danke!

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X