6

Доступ к вложенным объектам и массивам JavaScript по строковому пути

4

У меня есть такая структура данных:

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 ответ(ов)

6

Я только что создал эту функцию, основываясь на некотором похожем коде, который уже имел. Она, похоже, работает:

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 для некорректного индекса.

3

Теперь это поддерживается библиотекой 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'

Таким образом, вы можете безопасно получать вложенные значения из объекта, используя данный метод.

2

Вот решение, которое я использую:

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

Ограничения:

  • Нельзя использовать скобки ([]) для индексов массива — хотя указание индексов массива между разделительными символами (например, .) работает отлично, как показано выше.
2

Для тех, кто ищет решение для доступа к вложенным свойствам объекта в 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.

0

Функция, представленная в вашем коде, позволяет извлекать вложенные элементы из объектов и массивов, а также защищает от недопустимых значений. Вот более подробное объяснение:

Описание функции 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;
}

Как это работает:

  1. Входные параметры:

    • Функция принимает три аргумента: obj – это объект или массив, из которых вы хотите получить данные, path – строка с путем к целевому элементу, разделенная точками и def – значение по умолчанию на случай, если конечный элемент не найден.
  2. Итерация по пути:

    • path.split('.') разбивает строку пути на массив по разделителям '.'. Затем происходит итерация по каждому элементу пути.
    • Если на каком-либо этапе obj становится null или не является объектом, функция немедленно возвращает значение по умолчанию.
  3. Возврат результата:

    • После завершения цикла, если итоговое значение 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. Она поможет избежать ошибок, связанных с доступом к неопределённым свойствам, и обеспечит безопасное извлечение данных.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь