Доступ к вложенным объектам и массивам JavaScript по строковому пути
У меня есть такая структура данных:
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'
}
]
};
И я хотел бы получить доступ к данным, используя такие переменные:
var part1name = "part1.name";
var part2quantity = "part2.qty";
var part3name1 = "part3[0].name";
Переменная part1name
должна содержать значение someObject.part1.name
, то есть "Part 1". То же самое касается part2quantity
, которое должно быть равно 60.
Есть ли способ добиться этого с помощью чистого JavaScript или jQuery?
5 ответ(ов)
Я только что создал эту функцию, основываясь на некотором похожем коде, который уже имел. Она, похоже, работает:
Object.byString = function(o, s) {
s = s.replace(/\[(\w+)\]/g, '.$1'); // преобразуем индексы в свойства
s = s.replace(/^\./, ''); // убираем начальную точку
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;
}
Применение::
Object.byString(someObj, 'part3[0].name');
Вы можете увидеть рабочий демонстрационный пример на http://jsfiddle.net/alnitak/hEsys/
EDIT: Некоторые заметили, что этот код будет выдавать ошибку, если передать строку, в которой левые индексы не соответствуют правильно вложенному элементу в объекте. Это действительно важный момент, но, на мой взгляд, его лучше обрабатывать с помощью блока try / catch
при вызове, вместо того чтобы эта функция молча возвращала undefined
для некорректного индекса.
Теперь это поддерживается библиотекой lodash с помощью функции _.get(obj, property)
. Вы можете ознакомиться с документацией по адресу: https://lodash.com/docs#get
Пример из документации:
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'
Таким образом, вы можете безопасно получать вложенные значения из объекта, используя данный метод.
Вот решение, которое я использую:
function resolve(path, obj=self, separator='.') {
var properties = Array.isArray(path) ? path : path.split(separator)
return properties.reduce((prev, curr) => prev?.[curr], obj)
}
Пример использования:
// доступ к свойству в глобальной области видимости
resolve("document.body.style.width")
// или
resolve("style.width", document.body)
// доступ к индексам массива
// (someObject должен быть определён в вопросе)
resolve("part3.0.size", someObject) // возвращает '10'
// доступ к несуществующим свойствам
// возвращает undefined, когда промежуточные свойства не определены:
resolve('properties.that.do.not.exist', {hello:'world'})
// доступ к свойствам с необычными именами, изменяя разделитель
var obj = { object: { 'a.property.name.with.periods': 42 } }
resolve('object->a.property.name.with.periods', obj, '->') // возвращает 42
// доступ к свойствам с необычными ключами, передавая массив имен свойств
resolve(['object', 'a.property.name.with.periods'], obj) // возвращает 42
Ограничения:
- Нельзя использовать скобки (
[]
) для индексов массива — хотя указание индексов массива между разделительными символами (например,.
) работает отлично, как показано выше.
Для тех, кто ищет решение для доступа к вложенным свойствам объекта в JavaScript, вот несколько полезных функций, реализованных с использованием ES6 синтаксиса.
Получение значения по пути в объекте
Вы можете получить значение по строковому пути, используя метод reduce
. Если путь не ведет к значению, возвращается null
:
const resolvePath = (object, path, defaultValue) => path
.split('.')
.reduce((o, p) => o ? o[p] : defaultValue, object);
Пример использования:
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
Установка значения по пути
Чтобы установить значение по заданному пути, вы можете использовать следующую функцию:
const setPath = (object, path, value) => path
.split('.')
.reduce((o, p, i) => o[p] = path.split('.').length === ++i ? value : o[p] || {}, object);
Пример реализации:
let myVar = {};
setPath(myVar, 'a.b.c', 42); // устанавливает 42
console.log(myVar); // выведет {a: {b: {c: 42}}}
Доступ к элементам массива с использованием []
Если вам нужно работать с массивами и использовать квадратные скобки в строках путей, вы можете модифицировать resolvePath
следующим образом:
const resolvePath = (object, path, defaultValue) => path
.split(/[\.\[\]\'\"]/)
.filter(p => p)
.reduce((o, p) => o ? o[p] : defaultValue, object);
Пример использования:
const myVar = {a: {b: [{c: 1}]}};
resolvePath(myVar, 'a.b[0].c'); // вернет 1
resolvePath(myVar, 'a["b"][\'0\'].c'); // вернет 1
Эти функции помогут вам удобно работать с вложенными свойствами и массивами в объектах на JavaScript.
Функция, представленная в вашем коде, позволяет извлекать вложенные элементы из объектов и массивов, а также защищает от недопустимых значений. Вот более подробное объяснение:
Описание функции path
/**
* Извлекает вложенный элемент из объекта или массива
* @param {Object|Array} obj - объект или массив, из которого будет производиться извлечение
* @param {String} path - путь к элементу, разделенный точками
* @param {*} def - значение по умолчанию (если результат неопределен)
* @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;
}
Как это работает:
Входные параметры:
- Функция принимает три аргумента:
obj
– это объект или массив, из которых вы хотите получить данные,path
– строка с путем к целевому элементу, разделенная точками иdef
– значение по умолчанию на случай, если конечный элемент не найден.
- Функция принимает три аргумента:
Итерация по пути:
path.split('.')
разбивает строку пути на массив по разделителям '.'. Затем происходит итерация по каждому элементу пути.- Если на каком-либо этапе
obj
становитсяnull
или не является объектом, функция немедленно возвращает значение по умолчанию.
Возврат результата:
- После завершения цикла, если итоговое значение
obj
равноundefined
, возвращается значение по умолчанию, иначе возвращается найденный элемент.
- После завершения цикла, если итоговое значение
Примеры использования:
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]")`); // возвращает true
shouldBeEqualToNumber(`path(obj, "arr.length")`, 3); // возвращает 3
shouldBeTrue(`path(obj, "sp ace")`); // возвращает true
shouldBeEqualToString(`path(obj, "none.existed.prop", "fallback")`, "fallback"); // возвращает "fallback"
shouldBeTrue(`path(obj, "nested['dotted.str.ing']")`); // возвращает true
Выводы:
Данная функция является полезным инструментом для работы с вложенными структурами данных (объектами и массивами) в JavaScript. Она поможет избежать ошибок, связанных с доступом к неопределённым свойствам, и обеспечит безопасное извлечение данных.
Как получить доступ к вложенным объектам, массивам или JSON и обработать их?
Проверка существования вложенного ключа объекта JavaScript
В чем разница между String.slice и String.substring?
Существует ли ссылка на "последнюю" библиотеку jQuery в Google APIs?
Как создать диалог с кнопками "Ок" и "Отмена"