Accedere alle proprietà secondarie dell’object utilizzando una stringa notation dot

Sono temporaneamente bloccato con quello che sembra essere un problema JavaScript molto semplice, ma forse mi mancano solo le parole chiave di ricerca giuste!

Diciamo che abbiamo un object

var r = { a:1, b: {b1:11, b2: 99}}; 

Esistono diversi modi per accedere a 99:

 rbb2 r['b']['b2'] 

Quello che voglio è essere in grado di definire una stringa

 var s = "b.b2"; 

e quindi accedere al 99 utilizzando

 rs or r[s] //(which of course won't work) 

Un modo è quello di scrivere una funzione per esso che divide la stringa in punto e forse in modo ricorsivo / iterativo ottiene la proprietà. Ma c’è un modo più semplice / più efficace? Qualcosa di utile in una delle API di jQuery qui?

Ecco una funzione ingenua che ho scritto qualche tempo fa, ma funziona per le proprietà di base dell’object:

 function getDescendantProp(obj, desc) { var arr = desc.split("."); while(arr.length && (obj = obj[arr.shift()])); return obj; } console.log(getDescendantProp(r, "b.b2")); //-> 99 

Sebbene ci siano risposte che estendono questo per “consentire” l’accesso all’indice dell’array, ciò non è realmente necessario in quanto si possono semplicemente specificare indici numerici usando la notazione dot con questo metodo:

 getDescendantProp({ a: [ 1, 2, 3 ] }, 'a.2'); //-> 3 

dividere e ridurre mentre si passa l’object come valore initalValue

 var r = { a:1, b: {b1:11, b2: 99}}; var s = "b.b2"; var value = s.split('.').reduce(function(a, b) { return a[b]; }, r); console.log(value); 

Puoi usare i metodi lodash get () e set () .

ottenere

 var object = { 'a': [{ 'b': { 'c': 3 } }] }; _.get(object, 'a[0].b.c'); // → 3 

Ambientazione

 var object = { 'a': [{ 'b': { 'c': 3 } }] }; _.set(object, 'a[0].b.c', 4); console.log(object.a[0].bc); // → 4 

Se è ansible nel tuo scenario che puoi inserire l’intera variabile dell’array in una stringa, puoi utilizzare la eval() .

 var r = { a:1, b: {b1:11, b2: 99}}; var s = "rbb2"; alert(eval(s)); // 99 

Sento che la gente si agita per l’orrore

Estendendo la risposta di @ JohnB, ho aggiunto anche un valore setter. Controlla il plunkr a

http://plnkr.co/edit/lo0thC?p=preview

inserisci la descrizione dell'immagine qui

 function getSetDescendantProp(obj, desc, value) { var arr = desc ? desc.split(".") : []; while (arr.length && obj) { var comp = arr.shift(); var match = new RegExp("(.+)\\[([0-9]*)\\]").exec(comp); // handle arrays if ((match !== null) && (match.length == 3)) { var arrayData = { arrName: match[1], arrIndex: match[2] }; if (obj[arrayData.arrName] !== undefined) { if (typeof value !== 'undefined' && arr.length === 0) { obj[arrayData.arrName][arrayData.arrIndex] = value; } obj = obj[arrayData.arrName][arrayData.arrIndex]; } else { obj = undefined; } continue; } // handle regular things if (typeof value !== 'undefined') { if (obj[comp] === undefined) { obj[comp] = {}; } if (arr.length === 0) { obj[comp] = value; } } obj = obj[comp]; } return obj; } 

Questo è il più semplice che potessi fare:

 var accessProperties = function(object, string){ var explodedString = string.split('.'); for (i = 0, l = explodedString.length; i 

potresti anche fare

 var s = "['b'].b2"; var num = eval('r'+s); 

Non conosco una funzione API jQuery supportata, ma ho questa funzione:

  var ret = data; // Your object var childexpr = "b.b2"; // Your expression if (childexpr != '') { var childs = childexpr.split('.'); var i; for (i = 0; i < childs.length && ret != undefined; i++) { ret = ret[childs[i]]; } } return ret; 

Ho esteso la risposta di Andy E, in modo che possa anche gestire gli array:

 function getDescendantProp(obj, desc) { var arr = desc.split("."); //while (arr.length && (obj = obj[arr.shift()])); while (arr.length && obj) { var comp = arr.shift(); var match = new RegExp("(.+)\\[([0-9]*)\\]").exec(comp); if ((match !== null) && (match.length == 3)) { var arrayData = { arrName: match[1], arrIndex: match[2] }; if (obj[arrayData.arrName] != undefined) { obj = obj[arrayData.arrName][arrayData.arrIndex]; } else { obj = undefined; } } else { obj = obj[comp] } } return obj; } 

Ci sono probabilmente modi più efficienti per fare il Regex, ma è compatto.

Ora puoi fare cose come:

 var model = { "m1": { "Id": "22345", "People": [ { "Name": "John", "Numbers": ["07263", "17236", "1223"] }, { "Name": "Jenny", "Numbers": ["2", "3", "6"] }, { "Name": "Bob", "Numbers": ["12", "3333", "4444"] } ] } } // Should give you "6" var x = getDescendantProp(model, "m1.People[1].Numbers[2]"); 

Ecco un’estensione del codice di Andy E, che ricorre agli array e restituisce tutti i valori:

 function GetDescendantProps(target, pathString) { var arr = pathString.split("."); while(arr.length && (target = target[arr.shift()])){ if (arr.length && target.length && target.forEach) { // handle arrays var remainder = arr.join('.'); var results = []; for (var i = 0; i < target.length; i++){ var x = this.GetDescendantProps(target[i], remainder); if (x) results = results.concat(x); } return results; } } return (target) ? [target] : undefined; //single result, wrap in array for consistency } 

Quindi, dato questo target :

 var t = {a: {b: [ {'c':'x'}, {'not me':'y'}, {'c':'z'} ] } }; 

Noi abbiamo:

 GetDescendantProps(t, "abc") === ["x", "z"]; // true 

I test delle prestazioni per Andy E, Jason More’s e la mia soluzione sono disponibili su http://jsperf.com/propertyaccessor . Non esitate a eseguire test utilizzando il proprio browser per aggiungere ai dati raccolti.

La prognosi è chiara, la soluzione di Andy E è la più veloce di gran lunga!

Per chiunque sia interessato, ecco il codice per la mia soluzione alla domanda originale.

 function propertyAccessor(object, keys, array) { /* Retrieve an object property with a dot notation string. @param {Object} object Object to access. @param {String} keys Property to access using 0 or more dots for notation. @param {Object} [array] Optional array of non-dot notation strings to use instead of keys. @return {*} */ array = array || keys.split('.') if (array.length > 1) { // recurse by calling self return propertyAccessor(object[array.shift()], null, array) } else { return object[array] } } 

Risposta breve: No, non esiste una funzione nativa .access come desideri. Come hai giustamente menzionato, dovresti definire la tua funzione che divide la stringa e i loop / controlla le sue parti.

Ovviamente, quello che puoi sempre fare (anche se è considerato una ctriggers pratica) è usare eval() .

Piace

 var s = 'b.b2'; eval('r.' + s); // 99 

Ecco un modo un po ‘migliore della risposta di @ andy , in cui l’ obj (contesto) è facoltativo , ricade alla window se non fornito ..

 function getDescendantProp(desc, obj) { obj = obj || window; var arr = desc.split("."); while (arr.length && (obj = obj[arr.shift()])); return obj; };