Доступ к вложенным объектам JavaScript с помощью строкового ключа - PullRequest
378 голосов
/ 27 июня 2011

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

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, которое является «Часть 1». То же самое с part2quantity, который заполнен 60.

Есть ли способ достичь этого с помощью чистого JavaScript или JQuery?

Ответы [ 31 ]

7 голосов
/ 22 августа 2014

Здесь я предлагаю больше способов, которые во многих отношениях кажутся более быстрыми:

Вариант 1: Разделить строку на.или [или] или 'или ", перевернуть его, пропустить пустые элементы.

function getValue(path, origin) {
    if (origin === void 0 || origin === null) origin = self ? self : this;
    if (typeof path !== 'string') path = '' + path;
    var parts = path.split(/\[|\]|\.|'|"/g).reverse(), name; // (why reverse? because it's usually faster to pop off the end of an array)
    while (parts.length) { name=parts.pop(); if (name) origin=origin[name]; }
    return origin;
}

Вариант 2 (самый быстрый из всех, кроме eval): низкоуровневое сканирование символов (без регулярного выражения / разделения / и т. д.,только быстрое сканирование символов). Примечание. Этот код не поддерживает кавычки для индексов.

function getValue(path, origin) {
    if (origin === void 0 || origin === null) origin = self ? self : this;
    if (typeof path !== 'string') path = '' + path;
    var c = '', pc, i = 0, n = path.length, name = '';
    if (n) while (i<=n) ((c = path[i++]) == '.' || c == '[' || c == ']' || c == void 0) ? (name?(origin = origin[name], name = ''):(pc=='.'||pc=='['||pc==']'&&c==']'?i=n+2:void 0),pc=c) : name += c;
    if (i==n+2) throw "Invalid path: "+path;
    return origin;
} // (around 1,000,000+/- ops/sec)

Опция 3: ( new : опция 2расширен для поддержки кавычек - немного медленнее, но все еще быстро)

function getValue(path, origin) {
    if (origin === void 0 || origin === null) origin = self ? self : this;
    if (typeof path !== 'string') path = '' + path;
    var c, pc, i = 0, n = path.length, name = '', q;
    while (i<=n)
        ((c = path[i++]) == '.' || c == '[' || c == ']' || c == "'" || c == '"' || c == void 0) ? (c==q&&path[i]==']'?q='':q?name+=c:name?(origin?origin=origin[name]:i=n+2,name='') : (pc=='['&&(c=='"'||c=="'")?q=c:pc=='.'||pc=='['||pc==']'&&c==']'||pc=='"'||pc=="'"?i=n+2:void 0), pc=c) : name += c;
    if (i==n+2 || name) throw "Invalid path: "+path;
    return origin;
}

JSPerf: http://jsperf.com/ways-to-dereference-a-delimited-property-string/3

"eval (...)" все еще царь (производительностьесли это так). Если у вас есть пути свойств непосредственно под вашим контролем, не должно быть никаких проблем с использованием 'eval' (особенно, если требуется скорость). Если вы перетаскиваете пути свойств "по проводу" ( наline !? lol: P), тогда да, используйте что-то еще, чтобы быть в безопасности. Только идиот сказал бы, что никогда не используйте "eval" вообще, поскольку есть веские причины , когда использовать этоКроме того, «Он используется в JSON-парсере Дуга Крокфорда ». Если ввод безопасен, то никаких проблем нет. Используйте правильный инструмент для правильной работы, этоэто.

7 голосов
/ 27 июня 2011

Я думаю, что вы просите об этом:

var part1name = someObject.part1.name;
var part2quantity = someObject.part2.qty;
var part3name1 =  someObject.part3[0].name;

Вы могли бы просить об этом:

var part1name = someObject["part1"]["name"];
var part2quantity = someObject["part2"]["qty"];
var part3name1 =  someObject["part3"][0]["name"];

Оба из которых будут работать


Или, может быть, вы просите об этом

var partName = "part1";
var nameStr = "name";

var part1name = someObject[partName][nameStr];

Наконец, вы могли бы просить об этом

var partName = "part1.name";

var partBits = partName.split(".");

var part1name = someObject[partBits[0]][partBits[1]];
6 голосов
/ 26 февраля 2015

Подход Speigg очень аккуратный и чистый, хотя я нашел этот ответ, когда искал решение для доступа к свойствам области видимости AngularJS $ по строковому пути, и с небольшой модификацией он выполняет свою работу:

$scope.resolve = function( path, obj ) {
    return path.split('.').reduce( function( prev, curr ) {
        return prev[curr];
    }, obj || this );
}

Простопоместите эту функцию в свой корневой контроллер и используйте ее в любой дочерней области, например:

$scope.resolve( 'path.to.any.object.in.scope')
4 голосов
/ 08 июля 2018

На всякий случай, если кто-нибудь посетит этот вопрос в 2017 году или позже и ищет легкий для запоминания способ , вот подробный пост в блоге по Доступ к вложенным объектам в JavaScript без

Невозможно прочитать свойство 'foo' из неопределенного error

Доступ к вложенным объектам с помощью Array Reduce

Давайте рассмотрим пример структуры

const user = {
    id: 101,
    email: 'jack@dev.com',
    personalInfo: {
        name: 'Jack',
        address: [{
            line1: 'westwish st',
            line2: 'washmasher',
            city: 'wallas',
            state: 'WX'
        }]
    }
}

Чтобы иметь доступ к вложенным массивам, вы можете написать свой собственный утилита сокращения массива.

const getNestedObject = (nestedObj, pathArr) => {
    return pathArr.reduce((obj, key) =>
        (obj && obj[key] !== 'undefined') ? obj[key] : undefined, nestedObj);
}

// pass in your object structure as array elements
const name = getNestedObject(user, ['personalInfo', 'name']);

// to access nested array, just pass in array index as an element the path array.
const city = getNestedObject(user, ['personalInfo', 'address', 0, 'city']);
// this will return the city from the first address item.

Существует также превосходная минимальная библиотека для обработки типов typy , которая делаетвсе это для вас.

С помощью typy ваш код будет выглядеть следующим образом

const city = t(user, 'personalInfo.address[0].city').safeObject;

Отказ от ответственности: я являюсь автором этого пакета.

3 голосов
/ 26 декабря 2016

Простая функция, допускающая либо строку, либо путь к массиву.

function get(obj, path) {
  if(typeof path === 'string') path = path.split('.');

  if(path.length === 0) return obj;
  return get(obj[path[0]], path.slice(1));
}

const obj = {a: {b: {c: 'foo'}}};

console.log(get(obj, 'a.b.c')); //foo

OR

console.log(get(obj, ['a', 'b', 'c'])); //foo
3 голосов
/ 25 апреля 2015

Я еще не нашел пакет, который выполнял бы все операции со строковым путем, поэтому я в итоге написал свой собственный быстрый маленький пакет, который поддерживает insert (), get () (с возвратом по умолчанию), set ()и операции remove ().

Вы можете использовать точечные обозначения, квадратные скобки, числовые индексы, свойства строковых чисел и ключи с несловесными символами.Простое использование ниже:

> var jsocrud = require('jsocrud');

...

// Get (Read) ---
> var obj = {
>     foo: [
>         {
>             'key w/ non-word chars': 'bar'
>         }
>     ]
> };
undefined

> jsocrud.get(obj, '.foo[0]["key w/ non-word chars"]');
'bar'

https://www.npmjs.com/package/jsocrud

https://github.com/vertical-knowledge/jsocrud

2 голосов
/ 13 июня 2014

Для этого есть модуль npm: https://github.com/erictrinh/safe-access

Пример использования:

var access = require('safe-access');
access(very, 'nested.property.and.array[0]');
2 голосов
/ 01 марта 2016
/**
 * Access a deep value inside a object 
 * Works by passing a path like "foo.bar", also works with nested arrays like "foo[0][1].baz"
 * @author Victor B. https://gist.github.com/victornpb/4c7882c1b9d36292308e
 * Unit tests: http://jsfiddle.net/Victornpb/0u1qygrh/
 */
function getDeepVal(obj, path) {
    if (typeof obj === "undefined" || obj === null) return;
    path = path.split(/[\.\[\]\"\']{1,2}/);
    for (var i = 0, l = path.length; i < l; i++) {
        if (path[i] === "") continue;
        obj = obj[path[i]];
        if (typeof obj === "undefined" || obj === null) return;
    }
    return obj;
}

Работает с

getDeepVal(obj,'foo.bar')
getDeepVal(obj,'foo.1.bar')
getDeepVal(obj,'foo[0].baz')
getDeepVal(obj,'foo[1][2]')
getDeepVal(obj,"foo['bar'].baz")
getDeepVal(obj,"foo['bar']['baz']")
getDeepVal(obj,"foo.bar.0.baz[1]['2']['w'].aaa[\"f\"].bb")
1 голос
/ 15 декабря 2017

Хотя уменьшение - это хорошо, я удивлен, что никто не использовал для каждого:

function valueForKeyPath(obj, path){
        const keys = path.split('.');
        keys.forEach((key)=> obj = obj[key]);
        return obj;
    };

Тест

1 голос
/ 29 ноября 2018

Вдохновленный ответом @ webjay: https://stackoverflow.com/a/46008856/4110122

Я сделал эту функцию, которую вы можете использовать, чтобы Получить / Установить / Отменить любое значение в объекте

function Object_Manager(obj, Path, value, Action) 
{
    try
    {
        if(Array.isArray(Path) == false)
        {
            Path = [Path];
        }

        let level = 0;
        var Return_Value;
        Path.reduce((a, b)=>{
            level++;
            if (level === Path.length)
            {
                if(Action === 'Set')
                {
                    a[b] = value;
                    return value;
                }
                else if(Action === 'Get')
                {
                    Return_Value = a[b];
                }
                else if(Action === 'Unset')
                {
                    delete a[b];
                }
            } 
            else 
            {
                return a[b];
            }
        }, obj);
        return Return_Value;
    }

    catch(err)
    {
        console.error(err);
        return obj;
    }
}

Чтобы использовать это:

 // Set
 Object_Manager(Obj,[Level1,Level2,Level3],New_Value, 'Set');

 // Get
 Object_Manager(Obj,[Level1,Level2,Level3],'', 'Get');

 // Unset
 Object_Manager(Obj,[Level1,Level2,Level3],'', 'Unset');
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...