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



14 Answers

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

function resolve(path, obj=self, separator='.') {
    var properties = Array.isArray(path) ? path : path.split(separator)
    return properties.reduce((prev, curr) => prev && prev[curr], obj)
}

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

// accessing property path on global scope
resolve("document.body.style.width")
// or
resolve("style.width", document.body)

// accessing array indexes
// (someObject has been defined in the question)
resolve("part3.0.size", someObject) // returns '10'

// accessing non-existent properties
// returns undefined when intermediate properties are not defined:
resolve('properties.that.do.not.exist', {hello:'world'})

// accessing properties with unusual keys by changing the separator
var obj = { object: { 'a.property.name.with.periods': 42 } }
resolve('object->a.property.name.with.periods', obj, '->') // returns 42

// accessing properties with unusual keys by passing a property name array
resolve(['object', 'a.property.name.with.periods'], obj) // returns 42

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

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

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

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?




Вам придется самостоятельно разбирать строку:

function getProperty(obj, prop) {
    var parts = prop.split('.');

    if (Array.isArray(parts)) {
        var last = parts.pop(),
        l = parts.length,
        i = 1,
        current = parts[0];

        while((obj = obj[current]) && i < l) {
            current = parts[i];
            i++;
        }

        if(obj) {
            return obj[last];
        }
    } else {
        throw 'parts is not valid array';
    }
}

Это потребовало, чтобы вы также определяли индексы массивов с точечной нотацией:

var part3name1 = "part3.0.name";

Это упрощает синтаксический анализ.

DEMO




ES6 : только одна строка в Vanila JS (она возвращает null, если не находит, а не дает ошибку):

'path.string'.split('.').reduce((p,c)=>p&&p[c]||null, MyOBJ)

или например:

'a.b.c'.split('.').reduce((p,c)=>p&&p[c]||null, {a:{b:{c:1}}})

Для готовой к использованию функции, которая также распознает false, 0 и отрицательное число и принимает значения по умолчанию в качестве параметра:

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

Бонус :

Чтобы установить путь (запрошенным @ rob-gordon), вы можете использовать:

const setPath = (object, path, value) => path
   .split('.')
   .reduce((o,p) => o[p] = path.split('.').pop() === p ? value : o[p] || {}, object)

Пример:

let myVar = {}
setPath(myVar, 'a.b.c', 42) => 42
console.log(myVar) => {a: {b: {c: 42}}}

Массив доступа с [] :

const resolvePath = (object, path, defaultValue) => path
   .split(/[\.\[\]\'\"]/)
   .filter(p => p)
   .reduce((o, p) => o ? o[p] : defaultValue, object)

Exemple

const myVar = {a:{b:[{c:1}]}}
resolvePath(myVar,'a.b[0].c') => 1
resolvePath(myVar,'a["b"][\'0\'].c') => 1



Вы можете получить значение элемента глубокого объекта с точечной нотацией без какой-либо внешней библиотеки JavaScript с помощью простого следующего трюка:

new Function('_', 'return _.' + path)(obj);

В вашем случае для получения значения part1.name из someObject просто выполните:

new Function('_', 'return _.part1.name')(someObject);

Вот простая демонстрация скриптов: https://jsfiddle.net/harishanchu/oq5esowf/




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

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]];



Это один лайнер с lodash.

const deep = { l1: { l2: { l3: "Hello" } } };
const prop = "l1.l2.l3";
const val = _.reduce(prop.split('.'), function(result, value) { return result ? result[value] : undefined; }, deep);
// val === "Hello"

Или еще лучше ...

const val = _.get(deep, prop);

Или ES6 версии w / уменьшить ...

const val = prop.split('.').reduce((r, val) => { return r ? r[val] : undefined; }, deep);

Plunkr




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




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

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

var access = require('safe-access');
access(very, 'nested.property.and.array[0]');



/**
 * 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")



Если вам нужно получить доступ к разному вложенному ключу, не зная его во время кодирования (это будет тривиально для их адресации), вы можете использовать ассемблер нотации массива:

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

Они эквивалентны аксессуарам с точечной нотацией и могут варьироваться во время выполнения, например:

var part = 'part1';
var property = 'name';

var part1name = someObject[part][property];

эквивалентно

var part1name = someObject['part1']['name'];

или же

var part1name = someObject.part1.name;

Надеюсь, это касается вашего вопроса ...

РЕДАКТИРОВАТЬ

Я не буду использовать строку для создания своего рода запроса xpath для доступа к значению объекта. Поскольку вам нужно вызвать функцию для синтаксического анализа запроса и получения значения, я буду следовать другому пути (не:

var part1name = function(){ return this.part1.name; }
var part2quantity = function() { return this['part2']['qty']; }
var part3name1 =  function() { return this.part3[0]['name'];}

// usage: part1name.apply(someObject);

или, если вы не уверены в применении метода

var part1name = function(obj){ return obj.part1.name; }
var part2quantity = function(obj) { return obj['part2']['qty']; }
var part3name1 =  function(obj) { return obj.part3[0]['name'];}

// usage: part1name(someObject);

Функции короче, яснее, интерпретатор проверяет их для синтаксических ошибок и так далее.

Кстати, я чувствую, что простое задание, сделанное в нужное время, будет достаточно ...




Решения здесь предназначены только для доступа к глубоко вложенным ключам. Мне нужен был для доступа, добавления, изменения и удаления ключей. Вот что я придумал:

var deepAccessObject = function(object, path_to_key, type_of_function, value){
    switch(type_of_function){
        //Add key/modify key
        case 0: 
            if(path_to_key.length === 1){
                if(value)
                    object[path_to_key[0]] = value;
                return object[path_to_key[0]];
            }else{
                if(object[path_to_key[0]])
                    return deepAccessObject(object[path_to_key[0]], path_to_key.slice(1), type_of_function, value);
                else
                    object[path_to_key[0]] = {};
            }
            break;
        //delete key
        case 1:
            if(path_to_key.length === 1){
                delete object[path_to_key[0]];
                return true;
            }else{
                if(object[path_to_key[0]])
                    return deepAccessObject(object[path_to_key[0]], path_to_key.slice(1), type_of_function, value);
                else
                    return false;
            }
            break;
        default:
            console.log("Wrong type of function");
    }
};
  • path_to_key : путь в массиве. Вы можете заменить его на string_path.split(".") .
  • type_of_function : 0 для доступа (не передавать какое-либо значение в value ), 0 для добавления и изменения. 1 для удаления.



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

function get(obj, str) {
  return str.split(/\.|\[/g).map(function(crumb) {
    return crumb.replace(/\]$/, '').trim().replace(/^(["'])((?:(?!\1)[^\\]|\\.)*?)\1$/, (match, quote, str) => str.replace(/\\(\\)?/g, "$1"));
  }).reduce(function(obj, prop) {
    return obj ? obj[prop] : undefined;
  }, obj);
}



Работа с property или propertyOf Underscore :

var test = {
  foo: {
    bar: {
      baz: 'hello'
    }
  }
}
var string = 'foo.bar.baz';


// document.write(_.propertyOf(test)(string.split('.')))

document.write(_.property(string.split('.'))(test));
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.9.1/underscore-min.js"></script>

Удачи...




Построение ответа Алнитак:

if(!Object.prototype.byString){
  //NEW byString which can update values
Object.prototype.byString = function(s, v, o) {
  var _o = o || this;
      s = s.replace(/\[(\w+)\]/g, '.$1'); // CONVERT INDEXES TO PROPERTIES
      s = s.replace(/^\./, ''); // STRIP A LEADING DOT
      var a = s.split('.'); //ARRAY OF STRINGS SPLIT BY '.'
      for (var i = 0; i < a.length; ++i) {//LOOP OVER ARRAY OF STRINGS
          var k = a[i];
          if (k in _o) {//LOOP THROUGH OBJECT KEYS
              if(_o.hasOwnProperty(k)){//USE ONLY KEYS WE CREATED
                if(v !== undefined){//IF WE HAVE A NEW VALUE PARAM
                  if(i === a.length -1){//IF IT'S THE LAST IN THE ARRAY
                    _o[k] = v;
                  }
                }
                _o = _o[k];//NO NEW VALUE SO JUST RETURN THE CURRENT VALUE
              }
          } else {
              return;
          }
      }
      return _o;
  };

}

Это также позволяет вам установить значение!

Я также создал пакет npm и github




Related