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.

43voto

TheZver Punkte 1443

Funktioniert auch für Arrays / Arrays innerhalb des Objekts. Defensiv gegen ungültige Werte.

/**
 * Retrieve nested item from object/array
 * @param {Object|Array} obj
 * @param {String} path dot separated
 * @param {*} def default value ( if result undefined )
 * @returns {*}
 */
function path(obj, path, def){
    var i, len;

    for(i = 0,path = path.split('.'), len = path.length; i < len; i++){
        if(!obj || typeof obj !== 'object') return def;
        obj = obj[path[i]];
    }

    if(obj === undefined) return def;
    return obj;
}

//////////////////////////
//         TEST         //
//////////////////////////

var arr = [true, {'sp ace': true}, true]

var obj = {
  'sp ace': true,
  arr: arr,
  nested: {'dotted.str.ing': true},
  arr3: arr
}

shouldThrow(`path(obj, "arr.0")`);
shouldBeDefined(`path(obj, "arr[0]")`);
shouldBeEqualToNumber(`path(obj, "arr.length")`, 3);
shouldBeTrue(`path(obj, "sp ace")`);
shouldBeEqualToString(`path(obj, "none.existed.prop", "fallback")`, "fallback");
shouldBeTrue(`path(obj, "nested['dotted.str.ing'])`);

<script src="https://cdn.rawgit.com/coderek/e7b30bac7634a50ad8fd/raw/174b6634c8f57aa8aac0716c5b7b2a7098e03584/js-test.js"></script>

11 Stimmen

Danke, das ist die beste und leistungsfähigste Antwort - jsfiddle.net/Jw8XB/1

0 Stimmen

@Endless, ich möchte betonen, dass der Pfad die Elemente mit Punkten trennen sollte. Klammern werden nicht funktionieren. D.h. für den Zugriff auf das erste Element im Array verwenden Sie "0.sp ace".

33voto

Nick Grealy Punkte 21004

Das wird wahrscheinlich nie das Licht der Welt erblicken... aber hier ist es trotzdem.

  1. Ersetzen Sie [] Klammer-Syntax mit .
  2. Aufteilen auf . Zeichen
  3. Leere Zeichenfolgen entfernen
  4. Finden Sie den Pfad (sonst undefined )

(Um einen Pfad zu einem Objekt zu finden, verwenden Sie dies pathTo Lösung).

// "one liner" (ES6)

const deep_value = (obj, path) => 
path
    .replace(/\[|\]\.?/g, '.')
    .split('.')
    .filter(s => s)
    .reduce((acc, val) => acc && acc[val], obj);

// ... and that's it.

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'
        }
        // ...
    ],
    'pa[rt3' : [
        {
            'name': 'Part 3A',
            'size': '10',
            'qty' : '20'
        }
        // ...
    ]
};

console.log(deep_value(someObject, "part1.name"));               // Part 1
console.log(deep_value(someObject, "part2.qty"));                // 60
console.log(deep_value(someObject, "part3[0].name"));            // Part 3A
console.log(deep_value(someObject, "part3[0].....name"));        // Part 3A - invalid blank paths removed
console.log(deep_value(someObject, "pa[rt3[0].name"));           // undefined - name does not support square brackets

0 Stimmen

Beachten Sie, dass dies eine Menge ungültiger Pfade stillschweigend verschluckt, wie ...one...two... . Wenn man intelligente Verarbeitung betreiben will, kann man nicht einfach die [] con . müssen Sie alle ] dann ersetzen Sie alle [ die nicht am Anfang steht mit . und entfernen Sie dann alle [ links.

0 Stimmen

@vitaly-t - richtig. Siehe #3 - remove blank strings - die Lösung behandelt leere Pfade als ungültig. Und es ist auch richtig, dass es keine Pfadüberprüfung oder Unterstützung für eckige Klammern in Feldnamen gibt. Für eine robustere Lösung sollten Sie die Verwendung einer Bibliothek in Betracht ziehen, z. B. npmjs.com/package/jsonpath-plus o stedolan.github.io/jq

1 Stimmen

Ja, es gibt heute viele Bibliotheken, die eine ausführliche Syntaxanalyse durchführen. Leider hat das seinen Preis, denn es ist um ein Vielfaches langsamer als der einfache Indexansatz von a.0.b.1 was für JavaScript selbstverständlich und wesentlich schneller ist. Eine einfache Aufteilung reicht aus.

32voto

Shanimal Punkte 11276

Mit eval:

var part1name = eval("someObject.part1.name");

Wrap, um im Fehlerfall undefiniert zurückzugeben

function path(obj, path) {
    try {
        return eval("obj." + path);
    } catch(e) {
        return undefined;
    }
}

http://jsfiddle.net/shanimal/b3xTw/

Bitte gehen Sie mit gesundem Menschenverstand und Vorsicht vor, wenn Sie die Macht des Eval nutzen. Es ist ein bisschen wie mit einem Lichtschwert: Wenn Sie es einschalten, besteht eine 90-prozentige Chance, dass Sie sich ein Glied abtrennen. Es ist nicht für jeden geeignet.

8 Stimmen

Ob eval eine gute Idee ist, hängt davon ab, woher die Daten der Eigenschaftszeichenfolge stammen. Ich bezweifle, dass Sie sich Sorgen machen müssen, dass Hacker über einen statischen Aufruf vom Typ "var p='a.b.c';eval(p);" einbrechen. Dafür ist es eine sehr gute Idee.

27voto

Harish Ambady Punkte 10887

Mit dem folgenden einfachen Trick können Sie den Wert eines tiefen Objekts mit Punktschreibweise ohne externe JavaScript-Bibliothek ermitteln:

function objectGet(obj, path) { return new Function('_', 'return _.' + path)(obj); };

In Ihrem Fall, um den Wert von part1.name de someObject einfach tun:

objectGet(someObject, 'part1.name');

Hier ist ein einfaches Fiddle-Demo: https://jsfiddle.net/harishanchu/oq5esowf/

3 Stimmen

Function deep_value ( obj, path ) { return new Function( 'o', 'return o.' + path )( obj ); }

0 Stimmen

Es sah vielversprechend aus, aber es scheint die eval unter der Haube, und wird zurückgewiesen, wenn Sie CSP unsafe-eval geschützt haben. Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of script

15voto

James Punkte 2793

Es ist ein One-Liner mit Lodash.

const deep = { l1: { l2: { l3: "Hello" } } };
const prop = "l1.l2.l3";
const val = _.reduce(prop.split('.'), function(result, value) { return result ? result[value] : undefined; }, deep);
// val === "Hello"

Oder noch besser...

const val = _.get(deep, prop);

Oder ES6-Version mit Reduzierung...

const val = prop.split('.').reduce((r, val) => { return r ? r[val] : undefined; }, deep);

Plunkr

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