Свойства дочерних объектов объекта с использованием точечной нотации
Я временно застрял в том, что кажется очень простой проблемой 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)
Один из способов - написать для него функцию, которая разбивает строку на точку и, возможно, рекурсивно/итеративно получает свойство. Но есть ли более простой/более эффективный способ? Что-нибудь полезное в любом из jQuery API здесь?
Ответы
Ответ 1
Здесь наивная функция, которую я написал некоторое время назад, но она работает для основных свойств объекта:
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
Ответ 2
split и уменьшить при прохождении объект как 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);
Ответ 3
Если в вашем сценарии возможно, что вы можете поместить всю переменную массива в строку, вы можете использовать функцию eval()
.
var r = { a:1, b: {b1:11, b2: 99}};
var s = "r.b.b2";
alert(eval(s)); // 99
Я чувствую, как люди качаются в ужасе
Ответ 4
Вы можете использовать методы 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
Ответ 5
Расширение ответа @JohnB, я добавил значение setter. Проверьте plunkr на
http://plnkr.co/edit/lo0thC?p=preview
![enter image description here]()
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;
}
Ответ 6
Это самый простой способ:
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
Ответ 7
вы также можете сделать
var s = "['b'].b2";
var num = eval('r'+s);
Ответ 8
Я не знаю поддерживаемой функции jQuery API, но у меня есть эта функция:
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;
Ответ 9
Я расширил ответ Andy E, чтобы он мог также обрабатывать массивы:
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;
}
Есть, вероятно, более эффективные способы выполнения Regex, но они компактны.
Теперь вы можете делать такие вещи, как:
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]");
Ответ 10
Вот расширение кода Andy E, который переписывается в массивы и возвращает все значения:
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
Ответ 11
Тесты производительности для Andy E's, Jason More и моего собственного решения доступны в http://jsperf.com/propertyaccessor. Пожалуйста, не стесняйтесь запускать тесты, используя свой собственный браузер, чтобы добавить к собранным данным.
Прогноз ясен, решение Andy E является самым быстрым на сегодняшний день!
Для всех, кого это интересует, вот код для моего решения исходного вопроса.
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]
}
}
Ответ 12
Короткий ответ: Нет, нет встроенной функции .access
, как вы этого хотите. Как вы правильно упоминали, вам нужно будет определить свою собственную функцию, которая разбивает строку и циклы/проверки по ее частям.
Конечно, то, что вы всегда можете сделать (даже если это считается плохой практикой), это использовать eval()
.
Как
var s = 'b.b2';
eval('r.' + s); // 99
Ответ 13
Вот немного лучше, чем @andy, где obj
(контекст) необязательный, он возвращается к window
если не указано..
function getDescendantProp(desc, obj) {
obj = obj || window;
var arr = desc.split(".");
while (arr.length && (obj = obj[arr.shift()]));
return obj;
};