Доступ к дочерним свойствам объекта с помощью строки точечной нотации [дубликат]
этот вопрос уже есть ответ здесь:
- Доступ к вложенным объектам JavaScript с помощью строкового ключа 28 ответов
Я временно застрял с тем, что кажется очень простой проблемой JavaScript, но, возможно, мне просто не хватает правильных ключевых слов поиска!
скажем, у нас есть объект
var r = { a:1, b: {b1:11, b2: 99}};
есть несколько способов доступа к 99:
r.b.b2
r['b']['b2']
то, что я хочу, чтобы иметь возможность определить строку
var s = "b.b2";
а затем получить доступ к 99 с помощью
r.s or r[s] //(which of course won't work)
один из способов-написать для него функцию, которая разбивает строку на точку и, возможно, рекурсивно/итеративно получает свойство. Но есть ли более простой / эффективный способ? Что-нибудь полезное в любом из API jQuery здесь?
13 ответов:
вот наивная функция, которую я написал некоторое время назад, но она работает для основных свойств объекта:
function getDescendantProp(obj, desc) { var arr = desc.split("."); while(arr.length && (obj = obj[arr.shift()])); return obj; } console.log(getDescendantProp(r, "b.b2")); //-> 99
хотя есть ответы, которые расширяют это, чтобы" разрешить " доступ к индексу массива, это на самом деле не обязательно, так как вы можете просто указать числовые индексы, используя точечную нотацию с помощью этого метода:
getDescendantProp({ a: [ 1, 2, 3 ] }, 'a.2'); //-> 3
сплит и уменьшить при передаче объекта как
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);
обновление (спасибо за комментарий, опубликованный TeChn4K)
с синтаксисом ES6 он еще короче
var r = { a:1, b: {b1:11, b2: 99}}; var s = "b.b2"; var value = s.split('.').reduce((a, b) => a[b], r); console.log(value);
можно использовать lodash get() и set () методы.
начало
var object = { 'a': [{ 'b': { 'c': 3 } }] }; _.get(object, 'a[0].b.c'); // → 3
задание
var object = { 'a': [{ 'b': { 'c': 3 } }] }; _.set(object, 'a[0].b.c', 4); console.log(object.a[0].b.c); // → 4
Если в вашем сценарии возможно, что вы можете поместить всю переменную массива, которую вы ищете, в строку, вы можете использовать
расширяя ответ @JohnB, я также добавил значение сеттера. Проверьте plunkr на
http://plnkr.co/edit/lo0thC?p=preview
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; }
Это самое простое, что я мог сделать:
var accessProperties = function(object, string){ var explodedString = string.split('.'); for (i = 0, l = explodedString.length; i<l; i++){ object = object[explodedString[i]]; } return object; } var r = { a:1, b: {b1:11, b2: 99}}; var s = "b.b2"; var o = accessProperties(r, s); alert(o);//99
Я не знаю поддерживаемой функции API jQuery, но у меня есть эта функция:
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;
я расширил ответ Энди Э, так что он также может обрабатывать массивы:
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; }
есть, вероятно, более эффективные способы сделать регулярное выражение, но оно компактно.
теперь вы можете делать такие вещи, как:
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]");
здесь является расширением кода Энди Е, что повторяется в массивы и возвращает все значения:
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 }
этот
target
:var t = {a: {b: [ {'c':'x'}, {'not me':'y'}, {'c':'z'} ] } };
получаем:
GetDescendantProps(t, "a.b.c") === ["x", "z"]; // true
тесты производительности для Энди Э, Джейсона Мора и моего собственного решения доступны по адресу http://jsperf.com/propertyaccessor. пожалуйста, не стесняйтесь запускать тесты с помощью собственного браузера, чтобы добавить к собранным данным.
прогноз ясен, решение Энди Э является самым быстрым на сегодняшний день!
для всех, кто заинтересован, вот код для моего решения исходного вопроса.
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] } }
короткий ответ: Нет, уроженки
.access
функции, как вы хотите. Как вы правильно упомянули, вам нужно будет определить свою собственную функцию, которая разбивает строку и циклы/проверки по ее частям.конечно, то, что вы всегда можете сделать (даже если это считается плохой практикой), это использовать
eval()
.как
var s = 'b.b2'; eval('r.' + s); // 99