Можно ли добавить динамически именованные объекты в объект JavaScript? [Дубликат]

    static IEnumerable<IEnumerable<T>> TakeBatch<T>(IEnumerable<T> ts,int batchSize)
    {
        return from @group in ts.Select((x, i) => new { x, i }).ToLookup(xi => xi.i / batchSize)
               select @group.Select(xi => xi.x);
    }
316
задан Paul Roub 15 August 2017 в 16:57
поделиться

28 ответов

Я просто сделал это на основе некоторого аналогичного кода, который у меня уже был, он работает:

Object.byString = function(o, s) {
    s = s.replace(/\[(\w+)\]/g, '.$1'); // convert indexes to properties
    s = s.replace(/^\./, '');           // strip a leading dot
    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/

ИЗМЕНИТЬ, некоторые заметили, что этот код выдает ошибку, если передается строка, где самые левые индексы не соответствуют к правильно вложенной записи внутри объекта. Это актуальная проблема, но IMHO лучше всего обращается с блоком try / catch при вызове, вместо того, чтобы эта функция молча возвращать undefined для недопустимого индекса.

421
ответ дан Alnitak 15 August 2018 в 18:03
поделиться
  • 1
    Это прекрасно работает. Пожалуйста, внесите это в интернет, обернув его как пакет узлов. – t3dodson 13 January 2015 в 23:38
  • 2
    @ t3dodson Я только что сделал: github.com/capaj/object-resolve-path просто помните, что это не очень приятно, когда ваше имя свойства содержит «[]» само по себе. Regex заменит его на. и он не работает должным образом – Capaj 30 July 2015 в 21:57
  • 3
    качественный товар; используя библиотеку lodash, можно также сделать: _.get(object, nestedPropertyString); – ian 13 August 2015 в 12:49
  • 4
    Это, вероятно, затеряется в море комментариев, однако это ошибки, если вы попытаетесь устранить свойство, которого не существует. Итак, 'part3[0].name.iDontExist'. Добавление проверки, чтобы убедиться, что o является объектом в if in, устраняет проблему. (Как вы это делаете, это зависит от вас). См. Обновленную скрипту: jsfiddle.net/hEsys/418 – ste2425 17 November 2015 в 12:12
  • 5
    Это так золото. У нас есть приложение на основе конфигурации, и это полезно! Благодаря! – Christian Esperar 22 June 2016 в 15:52

с использованием eval:

var part1name = eval("someObject.part1.name");

wrap для возврата undefined при ошибке

function path(obj, path) {
    try {
        return eval("obj." + path);
    } catch(e) {
        return undefined;
    }
}

http://jsfiddle.net/shanimal/b3xTw/

При использовании силы eval используйте здравый смысл и осторожность. Это немного похоже на легкую саблю, если вы включите ее, есть вероятность 90% разбить конечность. Это не для всех.

19
ответ дан 5 revs, 2 users 91% 15 August 2018 в 18:03
поделиться
  • 1
    Независимо от того, является ли eval хорошей идеей, зависит от того, откуда берутся данные строки свойств. Я сомневаюсь, что у вас есть основания беспокоиться о том, что хакеры пробиваются через статический «var p =» a.b.c »; eval (p); & quot; тип вызова. Для вас это идеальная идея. – James Wilkins 21 August 2014 в 23:39

Недавно у вас был тот же вопрос и успешно использовался https://npmjs.org/package/tea-properties , который также set вложенных объектов / массивов:

get :

var o = {
  prop: {
    arr: [
      {foo: 'bar'}
    ]
  }
};

var properties = require('tea-properties');
var value = properties.get(o, 'prop.arr[0].foo');

assert(value, 'bar'); // true

set:

var o = {};

var properties = require('tea-properties');
properties.set(o, 'prop.arr[0].foo', 'bar');

assert(o.prop.arr[0].foo, 'bar'); // true
0
ответ дан abernier 15 August 2018 в 18:03
поделиться
  • 1
    «Этот модуль был прекращен. Используйте chaijs / pathval. & Quot; – Patrick Fisher 18 March 2014 в 02:09

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)

пример

const myVar = {a:{b:[{c:1}]}}
resolvePath(myVar,'a.b[0].c') => 1
resolvePath(myVar,'a["b"][\'0\'].c') => 1
26
ответ дан Adriano Spadoni 15 August 2018 в 18:03
поделиться
  • 1
    Мне нравится эта техника. Это действительно грязно, но я хотел использовать эту технику для назначения. let o = {a:{b:{c:1}}}; let str = 'a.b.c'; str.split('.').splice(0, str.split('.').length - 1).reduce((p,c)=>p&&p[c]||null, o)[str.split('.').slice(-1)] = "some new value"; – rob-gordon 28 June 2017 в 17:15
  • 2
    Мне нравится идея использования сокращения, но ваша логика кажется для значений 0, undefined и null. {a:{b:{c:0}}} возвращает null вместо 0. Возможно, явная проверка на null или undefined позволит устранить эти проблемы. (p,c)=>p === undefined || p === null ? undefined : p[c] – SmujMaiku 15 October 2017 в 17:31
  • 3
    Привет @SmujMaiku, "готов к использованию" функция возвращает правильно для '0', 'undefined' и 'null', я просто протестировал на консоли: resolvePath ({a: {b: {c: 0}}}, 'a.b.c', null) = & gt; 0; Он проверяет, существует ли ключ вместо самого значения, которое позволяет избежать более одной проверки – Adriano Spadoni 16 October 2017 в 09:18
  • 4
    здесь defaultValue не работает, используя Reflect.has(o, k) ? ... (ES6 Reflect.has ) работал, хотя – Andre Figueiredo 23 February 2018 в 13:31

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

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>

Удача ...

0
ответ дан Akash 15 August 2018 в 18:03
поделиться

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

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 для удаления.
0
ответ дан ayushgp 15 August 2018 в 18:03
поделиться

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

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

ИЛИ

console.log(get(obj, ['a', 'b', 'c'])); //foo
2
ответ дан Ben 15 August 2018 в 18:03
поделиться
  • 1
    Если вы собираетесь отправлять код в качестве ответа, пожалуйста, объясните why , код отвечает на вопрос. – Tieson T. 26 December 2016 в 06:44

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

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

var access = require('safe-access');
access(very, 'nested.property.and.array[0]');
2
ответ дан caleb 15 August 2018 в 18:03
поделиться

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

Вариант 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): Масштабирование с низким уровнем (без регулярного выражения / разделения / etc , просто быстрое сканирование символов). Примечание. Этот код не поддерживает кавычки для индексов.

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: (новый: параметр 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» (особенно если требуется скорость). Если вы тянете дорожки свойств «по проводам» ( на линии !? lol: P), то да, используйте что-то еще, чтобы быть в безопасности. идиот сказал бы, что никогда не будет использовать «eval» вообще, так как там есть веские причины , когда его использовать. Также «Он используется в парсере JSON Дуга Крокфорда ». Если вход безопасен, тогда никаких проблем вообще. Используйте правильный инструмент для правильной работы, вот и все.

8
ответ дан Community 15 August 2018 в 18:03
поделиться

Вы должны сами проанализировать строку:

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

61
ответ дан d.danailov 15 August 2018 в 18:03
поделиться
  • 1
    @Felix Kling: Ваше решение действительно дает мне то, что мне нужно. И я благодарю вас за это. Но Alnitak также предлагает разные способы и, похоже, работает. Поскольку я могу выбрать только один ответ, я выберу Alnitak ответ. Не то, чтобы его решение было лучше вас или что-то в этом роде. Во всяком случае, я очень ценю ваше решение и усилия, которые вы дали. – Komaruloh 27 June 2011 в 12:25
  • 2
    @ Komaruloh: О, я думал, что вы всегда можете отвечать на голосовые ответы по своему собственному вопросу .... в любом случае я был более или менее шучу, мне не нужна больше репутации;) Счастливое кодирование! – Felix Kling 27 June 2011 в 12:50
  • 3
    @Felix Kling: Вам нужно, по крайней мере, 15 репутации для голосования. :) Я считаю, что вам не нужно больше репутации с 69k +. благодаря – Komaruloh 27 June 2011 в 15:58
  • 4
    @Felix FWIW - преобразование из синтаксиса [] в синтаксис свойств довольно тривиально. – Alnitak 27 June 2011 в 17:19
  • 5
    Если вы измените цикл while на while (l > 0 && (obj = obj[current]) && i < l), этот код будет работать и для строк без точек. – Snea 17 August 2014 в 07:18

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

Невозможно прочитать свойство «foo» ошибки undefined

Доступ к вложенным объектам с использованием массива Уменьшить

Возьмем эту примерную структуру

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 что делает все это для вас.

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

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

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

0
ответ дан Dinesh Pandiyan 15 August 2018 в 18:03
поделиться

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

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;

Надеюсь, что этот адрес вопрос ...

EDIT

Я не буду использовать строку, чтобы передать вид запроса 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);

или, если вам неловко использовать метод apply

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

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

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

0
ответ дан Eineki 15 August 2018 в 18:03
поделиться
  • 1
    Интересно. Но в моем случае someObject инициализируется, но когда я присваиваю значение part1name. Я знаю только структуру. Вот почему я использую строку для описания структуры. И надеемся, что сможете использовать его для запроса моих данных из someObject. Спасибо, что поделились своей мыслью. :) – Komaruloh 27 June 2011 в 11:47
  • 2
    @Komaruloh: Думаю, вы напишете, что объект еще не инициализируется, когда вы создаете переменные. Кстати, я не понимаю, почему вы не можете выполнить задание в соответствующее время? – Eineki 27 June 2011 в 12:24
  • 3
    Извините, не упомянув, что someObject еще не инициализирован. По этой причине someObject извлекается через веб-службу. И я хочу иметь массив заголовков, которые состоят из part1name, part2qty и т. Д. Чтобы я мог просто пропустить массив заголовков и получить значение, которое я хотел, на основе значения part1name как «ключ» / путь к someObject. – Komaruloh 27 June 2011 в 12:47

Работает для массивов / массивов внутри объекта. Защита от недопустимых значений.

/**
 * Retrieve nested item from object/array
 * @param {Object|Array} obj
 * @param {String} path dot separated
 * @param {*} def default value ( if result undefined )
 * @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;
}

//////////////////////////
//         TEST         //
//////////////////////////

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]")`);
shouldBeEqualToNumber(`path(obj, "arr.length")`, 3);
shouldBeTrue(`path(obj, "sp ace")`);
shouldBeEqualToString(`path(obj, "none.existed.prop", "fallback")`, "fallback");
shouldBeTrue(`path(obj, "nested['dotted.str.ing'])`);
<script src="https://cdn.rawgit.com/coderek/e7b30bac7634a50ad8fd/raw/174b6634c8f57aa8aac0716c5b7b2a7098e03584/js-test.js"></script>

38
ответ дан Endless 15 August 2018 в 18:03
поделиться
  • 1
    Спасибо, это лучший и самый эффективный ответ - jsfiddle.net/Jw8XB/1 – Dominic Tobias 1 August 2013 в 00:00
  • 2
    @Endless, я хотел бы подчеркнуть, что путь должен разделять элементы с точками. Скобки не работают. То есть для доступа к первому элементу в использовании массива & quot; 0.sp ace ". – TheZver 27 March 2016 в 11:32

В то время как сокращение хорошее, я удивлен, что никто не использовал forEach:

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

Тест

0
ответ дан Flavien Volken 15 August 2018 в 18:03
поделиться
  • 1
    Вы даже не проверяете, существует ли obj [key]. Это ненадежно. – Carles Alcolea 22 July 2018 в 04:08
  • 2
    @CarlesAlcolea по умолчанию js не будет проверять, существует ли ключ объекта: a.b.c вызовет исключение, если в вашем объекте нет свойства b. Если вам нужно что-то молча отклонить неправильный путь ключа (который я не рекомендую), вы все равно можете заменить forEach на это keys.forEach((key)=> obj = (obj||{})[key]); – Flavien Volken 23 July 2018 в 07:57
  • 3
    Я пробегаю по нему объект, в котором отсутствовала фигурная скобка, мой плохой :) – Carles Alcolea 28 July 2018 в 23:59

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

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

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

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

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

11
ответ дан Harish Anchu 15 August 2018 в 18:03
поделиться
  • 1
    function deep_value (obj, path) {return new Function ('o', 'return o.' + path) (obj); } – ArcangelZith 29 July 2016 в 04:59

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

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]];
7
ответ дан Hogan 15 August 2018 в 18:03
поделиться
  • 1
    Я думаю, что OP просит последнее решение. Тем не менее, строки не имеют метода Split, а скорее split. – duri 27 June 2011 в 11:37
  • 2
    Фактически я спрашивал последний. Переменная partName заполняется строкой, указывающей значение ключевой структуры. Ваше решение кажется разумным. Однако мне может потребоваться изменить для расширенной глубины в данных, например, уровень 4-5 и более. И мне интересно, смогу ли я рассматривать массив и объект равномерно с этим? – Komaruloh 27 June 2011 в 11:38

Теперь это поддерживается 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'
111
ответ дан Ian Walker-Sperber 15 August 2018 в 18:03
поделиться
  • 1
    Это должен быть единственный принятый ответ, потому что это единственный, который работает как для синтаксиса точек, так и для скобок, и он не прерывается, когда мы имеем «[]» в строке ключа в пути. – Capaj 4 August 2015 в 02:12
  • 2
    Эта. Кроме того, он поддерживает _.set(...) – Josh C. 22 August 2017 в 06:23
  • 3
    что произойдет, если объект не найден? – DDave 31 October 2017 в 17:58
  • 4
    @DDave, если значение, переданное как объект, не определено или не является объектом, _.get будет показывать то же поведение, что и при отсутствии ключа в предоставленном объекте. например _.get(null, "foo") -> undefined, _.get(null, "foo", "bar") -> "bar". Однако это поведение не определено в документах, поэтому они могут быть изменены. – Ian Walker-Sperber 2 November 2017 в 01:40
  • 5
    @ Капай ты киддин? А кто не хочет / не может использовать lodash? – Andre Figueiredo 19 February 2018 в 22:21

Это один лайнер с 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 с уменьшением ...

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

Plunkr

4
ответ дан James 15 August 2018 в 18:03
поделиться

Вместо строки можно использовать массив для адресации вложенных объектов и массивов, например: ["my_field", "another_field", 0, "last_field", 10]

Вот пример, который изменил бы поле на основе этого представления массива. Я использую что-то подобное в response.js для управляемых полей ввода, которые изменяют состояние вложенных структур.

let state = {
        test: "test_value",
        nested: {
            level1: "level1 value"
        },
        arr: [1, 2, 3],
        nested_arr: {
            arr: ["buh", "bah", "foo"]
        }
    }

function handleChange(value, fields) {
    let update_field = state;
    for(var i = 0; i < fields.length - 1; i++){
        update_field = update_field[fields[i]];
    }
    update_field[fields[fields.length-1]] = value;
}

handleChange("update", ["test"]);
handleChange("update_nested", ["nested","level1"]);
handleChange(100, ["arr",0]);
handleChange('changed_foo', ["nested_arr", "arr", 3]);
console.log(state);
0
ответ дан Jodo 15 August 2018 в 18:03
поделиться

Как насчет этого решения:

setJsonValue: function (json, field, val) {
  if (field !== undefined){
    try {
      eval("json." + field + " = val");
    }
    catch(e){
      ;
    }
  }  
}

И этот, для получения:

getJsonValue: function (json, field){
  var value = undefined;
  if (field !== undefined) {
    try {
      eval("value = json." + field);
    } 
    catch(e){
      ;
    }
  }
  return value;
};

Вероятно, некоторые будут считать их небезопасными, но они должны быть намного быстрее, разбор строки.

-1
ответ дан Jonan Georgiev 15 August 2018 в 18:03
поделиться

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

3
ответ дан Kyle 15 August 2018 в 18:03
поделиться

Подход 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')
6
ответ дан nesinervink 15 August 2018 в 18:03
поделиться

Это, вероятно, никогда не увидит свет дня ... но здесь все равно.

  1. Заменить [] синтаксис скобки с помощью .
  2. Разделить на ' character
  3. Найти путь (в противном случае undefined)

// "one liner" (ES6)

const deep_value = (obj, path) => 
  path
    .replace(/\[|\]\.?/g, '.')
    .split('.')
    .reduce((acc, val) => acc && acc[val], obj);
    
// ... and that's it.

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'
        }
        // ...
    ]
};

console.log(deep_value(someObject, "part1.name"));               // Part 1
console.log(deep_value(someObject, "part2.qty"));                // 60
console.log(deep_value(someObject, "part3[0].name"));            // Part 3A

1
ответ дан Nick Grealy 15 August 2018 в 18:03
поделиться

// (IE9+) Two steps

var pathString = "[0]['property'].others[3].next['final']";
var obj = [{
  property: {
    others: [1, 2, 3, {
      next: {
        final: "SUCCESS"
      }
    }]
  }
}];

// Turn string to path array
var pathArray = pathString
    .replace(/\[["']?([\w]+)["']?\]/g,".$1")
    .split(".")
    .splice(1);

// Add object prototype method
Object.prototype.path = function (path) {
  try {
    return [this].concat(path).reduce(function (f, l) {
      return f[l];
    });
  } catch (e) {
    console.error(e);
  }
};

// usage
console.log(obj.path(pathArray));
console.log(obj.path([0,"doesNotExist"]));

0
ответ дан Oboo Chin 15 August 2018 в 18:03
поделиться

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

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

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

  • Нельзя использовать скобки ([]) для индексов массива, хотя указание индексов массива между токеном разделителя (например, .) отлично работает, как показано выше.
120
ответ дан speigg 15 August 2018 в 18:03
поделиться

Создание ответа Алнитак:

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 package и github с этим также

-1
ответ дан Tamb 15 August 2018 в 18:03
поделиться

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

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);
}
0
ответ дан Vincent 15 August 2018 в 18:03
поделиться
/**
 * 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
ответ дан Vitim.us 15 August 2018 в 18:03
поделиться
Другие вопросы по тегам:

Похожие вопросы: