Преобразовать строку JavaScript в точечной нотации в ссылку на объект
Учитывая объект JS: var obj = { a: { b: '1', c: '2' } }
и строка "a.b"
Как я могу преобразовать строку в точечную запись, чтобы я мог пойти: var val = obj.a.b
;
Если строка была просто "я", я могу использовать obj[a]
но это сложнее. Я предполагаю, что есть какой-то простой метод, но в настоящее время он избегает.
36 ответов
недавнее примечание: хотя я польщен, что этот ответ получил много голосов, я также несколько испуган. Если нужно преобразовать строки с точечными обозначениями, такие как "xabc", в ссылки, это, вероятно, признак того, что происходит что-то очень неправильное (если, возможно, вы не выполняете какую-то странную десериализацию). Это излишне, потому что это ненужное метапрограммирование, а также несколько нарушает функциональный стиль кодирования без побочных эффектов. Кроме того, ожидайте значительных падений производительности, если вы делаете это больше, чем нужно (например, как форма приложения по умолчанию для передачи объектов и разыменования их). Если по какой-то причине это js на стороне сервера, то обычно выполняется для очистки входных данных. Новичкам, которые находят свой путь к этому ответу, следует вместо этого рассмотреть возможность работы с представлениями массивов, например, ['x','a','b','c'], или даже чем-то более прямым / простым / простым, если это возможно, например, не потерять отслеживание самих ссылок, или, может быть, какой-то ранее существовавший уникальный идентификатор и т. д.
Вот изящная однострочная, которая в 10 раз короче, чем другие решения:
function index(obj,i) {return obj[i]}
'a.b.etc'.split('.').reduce(index, obj)
[править] Или в ECMAScript 6:
'a.b.etc'.split('.').reduce((o,i)=>o[i], obj)
(Не то, чтобы я думал, что eval всегда плох, как другие предполагают, что это так (хотя обычно это так), тем не менее, эти люди будут рады, что этот метод не использует eval. obj.a.b.etc
дано obj
и строка "a.b.etc"
.)
В ответ на тех, кто все еще боится использования reduce
несмотря на то, что он находится в стандарте ECMA-262 (5-е издание), вот рекурсивная реализация из двух строк:
function multiIndex(obj,is) { // obj,['1','2','3'] -> ((obj['1'])['2'])['3']
return is.length ? multiIndex(obj[is[0]],is.slice(1)) : obj
}
function pathIndex(obj,is) { // obj,'1.2.3' -> multiIndex(obj,['1','2','3'])
return multiIndex(obj,is.split('.'))
}
pathIndex('a.b.etc')
В зависимости от оптимизаций, выполняемых JS-компилятором, вы можете убедиться, что любые вложенные функции не переопределяются при каждом вызове с помощью обычных методов (помещая их в замыкание, объект или глобальное пространство имен).
редактировать:
Чтобы ответить на интересующий вопрос в комментариях:
как бы ты превратил это в сеттер? Не только вернуть значения по пути, но и установить их, если новое значение отправлено в функцию? - Swader 28 июня в 21:42
(sidenote: к сожалению, не может вернуть объект с помощью Setter, так как это нарушило бы соглашение о вызовах; кажется, что вместо этого commenter ссылается на общую функцию стиля сеттера с такими побочными эффектами, как index(obj,"a.b.etc", value)
дела obj.a.b.etc = value
.)
reduce
стиль не очень подходит для этого, но мы можем изменить рекурсивную реализацию:
function index(obj,is, value) {
if (typeof is == 'string')
return index(obj,is.split('.'), value);
else if (is.length==1 && value!==undefined)
return obj[is[0]] = value;
else if (is.length==0)
return obj;
else
return index(obj[is[0]],is.slice(1), value);
}
Демо-версия:
> obj = {a:{b:{etc:5}}}
> index(obj,'a.b.etc')
5
> index(obj,['a','b','etc']) #works with both strings and lists
5
> index(obj,'a.b.etc', 123) #setter-mode - third argument (possibly poor form)
123
> index(obj,'a.b.etc')
123
... хотя лично я бы рекомендовал сделать отдельную функцию setIndex(...)
, Я хотел бы закончить дополнительным замечанием, что первоначальный вопросник мог (должен?) Работать с массивами индексов (которые они могут получить из .split
), а не строки; хотя обычно нет ничего плохого с удобной функцией.
Комментатор спросил:
как насчет массивов? что-то вроде "ab[4].cd[1][2][3]"? -AlexS
Javascript - очень странный язык; в общем случае объекты могут иметь только строки в качестве ключей своих свойств, например, если x
был общий объект, как x={}
, затем x[1]
станет x["1"]
... вы правильно прочитали... да...
Javascript Arrays (которые сами являются экземплярами Object) специально поддерживают целочисленные ключи, даже если вы можете сделать что-то вроде x=[]; x["puppy"]=5;
,
Но в целом (и есть исключения), x["somestring"]===x.somestring
(когда это разрешено; вы не можете сделать x.123
).
(Имейте в виду, что любой JS-компилятор, который вы используете, может выбрать, возможно, скомпилировать их для более разумных представлений, если он может доказать, что он не будет нарушать спецификацию.)
Таким образом, ответ на ваш вопрос будет зависеть от того, предполагаете ли вы, что эти объекты принимают только целые числа (из-за ограничения в вашей проблемной области), или нет. Давайте предположим, что нет. Тогда допустимое выражение - это объединение базового идентификатора и некоторых .identifier
с плюс некоторые ["stringindex"]
s
Это будет тогда эквивалентно a["b"][4]["c"]["d"][1][2][3]
хотя мы, вероятно, должны также поддержать a.b["c\"validjsstringliteral"][3]
, Вы должны проверить раздел грамматики ecmascript для строковых литералов, чтобы увидеть, как анализировать действительный строковый литерал. Технически вы также хотели бы проверить (в отличие от моего первого ответа), что a
является допустимым идентификатором JavaScript.
Простой ответ на ваш вопрос, хотя, если ваши строки не содержат запятых или скобок, будет просто соответствовать последовательности символов длиной 1+, которых нет в наборе ,
или же [
или же ]
:
> "abc[4].c.def[1][2][\"gh\"]".match(/[^\]\[.]+/g)
// ^^^ ^ ^ ^^^ ^ ^ ^^^^^
["abc", "4", "c", "def", "1", "2", ""gh""]
Если ваши строки не содержат escape-символов или "
символов, и поскольку IdentifierNames являются подъязыком StringLiterals (я думаю???), вы можете сначала преобразовать свои точки в []:
> var R=[], demoString="abc[4].c.def[1][2][\"gh\"]";
> for(var match,matcher=/^([^\.\[]+)|\.([^\.\[]+)|\["([^"]+)"\]|\[(\d+)\]/g;
match=matcher.exec(demoString); ) {
R.push(Array.from(match).slice(1).filter(x=>x!==undefined)[0]);
// extremely bad code because js regexes are weird, don't use this
}
> R
["abc", "4", "c", "def", "1", "2", "gh"]
Конечно, всегда будьте осторожны и никогда не доверяйте своим данным. Некоторые плохие способы сделать это, которые могут работать для некоторых случаев использования, также включают в себя:
// hackish/wrongish; preprocess your string into "a.b.4.c.d.1.2.3", e.g.:
> yourstring.replace(/]/g,"").replace(/\[/g,".").split(".")
"a.b.4.c.d.1.2.3" //use code from before
Специальный 2018 год редактировать:
Давайте пройдем полный круг и сделаем самое неэффективное, ужасно запрограммированное решение, которое мы можем придумать... в интересах синтаксической чистоты. С объектами ES6 Proxy!... Давайте также определим некоторые свойства, которые (imho прекрасны и замечательны, но) могут нарушать неправильно написанные библиотеки. Возможно, вам следует с осторожностью использовать это, если вы заботитесь о работоспособности, здравомыслии (своих или чужих), своей работе и т. Д.
// [1,2,3][-1]==3 (or just use .slice(-1)[0])
if (![1][-1])
Object.defineProperty(Array.prototype, -1, {get() {return this[this.length-1]}}); //credit to caub
// WARNING: THIS XTREME™ RADICAL METHOD IS VERY INEFFICIENT,
// ESPECIALLY IF INDEXING INTO MULTIPLE OBJECTS,
// because you are constantly creating wrapper objects on-the-fly and,
// even worse, going through Proxy i.e. runtime ~reflection, which prevents
// compiler optimization
// Proxy handler to override obj[*]/obj.* and obj[*]=...
var hyperIndexProxyHandler = {
get: function(obj,key, proxy) {
return key.split('.').reduce((o,i)=>o[i], obj);
},
set: function(obj,key,value, proxy) {
var keys = key.split('.');
var beforeLast = keys.slice(0,-1).reduce((o,i)=>o[i], obj);
beforeLast[keys[-1]] = value;
},
has: function(obj,key) {
//etc
}
};
function hyperIndexOf(target) {
return new Proxy(target, hyperIndexProxyHandler);
}
Демо-версия:
var obj = {a:{b:{c:1, d:2}}};
console.log("obj is:", JSON.stringify(obj));
var objHyper = hyperIndexOf(obj);
console.log("(proxy override get) objHyper['a.b.c'] is:", objHyper['a.b.c']);
objHyper['a.b.c'] = 3;
console.log("(proxy override set) objHyper['a.b.c']=3, now obj is:", JSON.stringify(obj));
console.log("(behind the scenes) objHyper is:", objHyper);
if (!({}).H)
Object.defineProperties(Object.prototype, {
H: {
get: function() {
return hyperIndexOf(this); // TODO:cache as a non-enumerable property for efficiency?
}
}
});
console.log("(shortcut) obj.H['a.b.c']=4");
obj.H['a.b.c'] = 4;
console.log("(shortcut) obj.H['a.b.c'] is obj['a']['b']['c'] is", obj.H['a.b.c']);
Выход:
obj is: {"a": {"b": {"c": 1, "d": 2}}}
(получить переопределение прокси) objHyper['abc']: 1
(набор переопределений прокси) objHyper['abc']=3, теперь obj: {"a": {"b": {"c": 3, "d": 2}}}
(за кадром) objHyper это: Прокси {a: {…}}
(ярлык) obj.H['abc'] = 4
(ярлык) obj.H['abc'] is obj['a']['b']['c']: 4
неэффективная идея: Вы можете изменить вышеприведенное для отправки на основе входного аргумента; либо использовать .match(/[^\]\[.]+/g)
метод поддержки obj['keys'].like[3]['this']
, или если instanceof Array
, а затем просто принять массив в качестве ввода, как keys = ['a','b','c']; obj.H[keys]
,
По предположению, что, возможно, вы хотите обрабатывать неопределенные индексы в "более мягком" стиле NaN (например, index({a:{b:{c:...}}}, 'a.x.c')
вернуть неопределенный, а не необработанный TypeError)...:
1) Это имеет смысл с точки зрения "мы должны вернуть undefined вместо того, чтобы выбросить ошибку" в ситуации с 1-мерным индексом ({})['eg']==undefined, поэтому "мы должны вернуть undefined вместо того, чтобы бросать ошибка "в N-мерной ситуации.
2) Это не имеет смысла с точки зрения того, что мы делаем x['a']['x']['c']
, что приведет к ошибке TypeError в приведенном выше примере.
Тем не менее, вы бы сделали эту работу, заменив функцию сокращения на:
(o,i)=>o===undefined?undefined:o[i]
, или же (o,i)=>(o||{})[i]
,
(Вы можете сделать это более эффективным, используя цикл for и прерывая / возвращая всякий раз, когда подрезультат, в который будет добавлен следующий индекс, не определен, или используя try-catch, если вы ожидаете, что такие ошибки будут достаточно редкими.)
Если вы можете использовать lodash, есть функция, которая делает именно это:
_.get (объект, путь, [defaultValue])
var val = _.get(obj, "a.b");
2021 г.
Вам не нужно подключать другую зависимость каждый раз, когда вам нужны новые возможности в вашей программе. Современный JS очень эффективен, и оператор необязательной цепочки
?.
теперь широко поддерживается и делает эту задачу чертовски простой.
С помощью одной строчки кода мы можем написать
get
который принимает входной объект,
t
и струна
path
. Работает для объектов и массивов любого уровня вложенности -
"hello"
"world"
undefined
Вы также можете использовать lodash.get
Вы просто устанавливаете этот пакет (npm i --save lodash.get) и затем используете его следующим образом:
const get = require('lodash.get');
const myObj = { user: { firstName: 'Stacky', lastName: 'Overflowy' }, id: 123 };
console.log(get(myObj, 'user.firstName')); // prints Stacky
console.log(get(myObj, 'id')); //prints 123
//You can also update values
get(myObj, 'user').firstName = John;
Немного более сложный пример с рекурсией.
function recompose(obj,string){
var parts = string.split('.');
var newObj = obj[parts[0]];
if(parts[1]){
parts.splice(0,1);
var newString = parts.join('.');
return recompose(newObj,newString);
}
return newObj;
}
var obj = { a: { b: '1', c: '2', d:{a:{b:'blah'}}}};
alert(recompose(obj,'a.d.a.b')); //blah
Я предлагаю разделить путь, повторить его и уменьшить объект, который у вас есть. Это предложение работает со значением по умолчанию для отсутствующих свойств.
function getValue(object, keys) {
return keys.split('.').reduce(function (o, k) {
return (o || {})[k];
}, object);
}
console.log(getValue({ a: { b: '1', c: '2' } }, 'a.b'));
console.log(getValue({ a: { b: '1', c: '2' } }, 'foo.bar.baz'));
Если вы ожидаете разыменования одного и того же пути много раз, создание функции для каждого пути точечной нотации на самом деле имеет наилучшую производительность (подробно о тестах производительности, на которые ссылается Джеймс Уилкинс в комментариях выше).
var path = 'a.b.x';
var getter = new Function("obj", "return obj." + path + ";");
getter(obj);
Использование конструктора Function имеет некоторые из тех же недостатков, что и eval(), с точки зрения безопасности и производительности в худшем случае, но IMO - это инструмент, который недостаточно используется для случаев, когда вам требуется сочетание экстремального динамизма и высокой производительности. Я использую эту методологию для построения функций фильтра массива и вызова их внутри дайджест-цикла AngularJS. Мои профили последовательно показывают шаг array.filter(), который занимает менее 1 мс для разыменования и фильтрации около 2000 сложных объектов с использованием динамически определенных путей глубиной 3-4 уровня.
Подобная методология может быть использована для создания функций-установщиков, конечно:
var setter = new Function("obj", "newval", "obj." + path + " = newval;");
setter(obj, "some new val");
Много лет со дня первоначального поста. Теперь есть отличная библиотека под названием "объект-путь". https://github.com/mariocasciaro/object-path
Доступно на NPM и BOWER https://www.npmjs.com/package/object-path
Это так же просто, как:
objectPath.get(obj, "a.c.1"); //returns "f"
objectPath.set(obj, "a.j.0.f", "m");
И работает для глубоко вложенных свойств и массивов.
Другие предложения немного загадочны, поэтому я решил внести свой вклад:
Object.prop = function(obj, prop, val){
var props = prop.split('.')
, final = props.pop(), p
while(p = props.shift()){
if (typeof obj[p] === 'undefined')
return undefined;
obj = obj[p]
}
return val ? (obj[final] = val) : obj[final]
}
var obj = { a: { b: '1', c: '2' } }
// get
console.log(Object.prop(obj, 'a.c')) // -> 2
// set
Object.prop(obj, 'a.c', function(){})
console.log(obj) // -> { a: { b: '1', c: [Function] } }
Вы можете использовать библиотеку, доступную на npm, что упрощает этот процесс. https://www.npmjs.com/package/dot-object
var dot = require('dot-object');
var obj = {
some: {
nested: {
value: 'Hi there!'
}
}
};
var val = dot.pick('some.nested.value', obj);
console.log(val);
// Result: Hi there!
Обратите внимание, если вы уже используете Lodash, вы можете использовать property
или же get
функции:
var obj = { a: { b: '1', c: '2' } };
_.property('a.b')(obj); // => 1
_.get(obj, 'a.b'); // => 1
Подчеркивание также имеет property
функция, но она не поддерживает точечную запись.
var a = { b: { c: 9 } };
function value(layer, path, value) {
var i = 0,
path = path.split('.');
for (; i < path.length; i++)
if (value != null && i + 1 === path.length)
layer[path[i]] = value;
layer = layer[path[i]];
return layer;
};
value(a, 'b.c'); // 9
value(a, 'b.c', 4);
value(a, 'b.c'); // 4
Это много кода по сравнению с гораздо более простым eval
способ сделать это, но, как говорит Саймон Уиллисон, вы никогда не должны использовать eval.
Также JSFiddle.
Я расширил элегантный ответ с помощью ninjagecko, чтобы функция обрабатывала как точечные, так и / или массивные ссылки, и чтобы пустая строка вызывала возврат родительского объекта.
Ну вот:
string_to_ref = function (object, reference) {
function arr_deref(o, ref, i) { return !ref ? o : (o[ref.slice(0, i ? -1 : ref.length)]) }
function dot_deref(o, ref) { return ref.split('[').reduce(arr_deref, o); }
return !reference ? object : reference.split('.').reduce(dot_deref, object);
};
Смотрите мой рабочий пример jsFiddle здесь: http://jsfiddle.net/sc0ttyd/q7zyd/
Вы можете получить значение элемента объекта с помощью точечной нотации с одной строкой кода:
new Function('_', 'return _.' + path)(obj);
В вашем случае:
var obj = { a: { b: '1', c: '2' } }
var val = new Function('_', 'return _.a.b')(obj);
Для простоты вы можете написать такую функцию:
function objGet(obj, path){
return new Function('_', 'return _.' + path)(obj);
}
Объяснение:
Конструктор Function создает новый объект Function. В JavaScript каждая функция на самом деле является объектом Function. Синтаксис для создания функции явно с помощью конструктора функций:
new Function ([arg1[, arg2[, ...argN]],] functionBody)
где arguments(arg1 to argN)
должна быть строка, которая соответствует действительному идентификатору javaScript и functionBody
это строка, содержащая операторы javaScript, содержащие определение функции.
В нашем случае мы используем преимущество строковой функции body для извлечения члена объекта с точечной нотацией.
Надеюсь, поможет.
Используйте эту функцию:
function dotToObject(data) {
function index(parent, key, value) {
const [mainKey, ...children] = key.split(".");
parent[mainKey] = parent[mainKey] || {};
if (children.length === 1) {
parent[mainKey][children[0]] = value;
} else {
index(parent[mainKey], children.join("."), value);
}
}
const result = Object.entries(data).reduce((acc, [key, value]) => {
if (key.includes(".")) {
index(acc, key, value);
} else {
acc[key] = value;
}
return acc;
}, {});
return result;
}
module.exports = { dotToObject };
Бывший:
const user = {
id: 1,
name: 'My name',
'address.zipCode': '123',
'address.name': 'Some name',
'address.something.id': 1,
}
const mappedUser = dotToObject(user)
console.log(JSON.stringify(mappedUser, null, 2))
Выход:
{
"id": 1,
"name": "My name",
"address": {
"zipCode": "123",
"name": "Some name",
"something": {
"id": 1
}
}
}
var find = function(root, path) {
var segments = path.split('.'),
cursor = root,
target;
for (var i = 0; i < segments.length; ++i) {
target = cursor[segments[i]];
if (typeof target == "undefined") return void 0;
cursor = target;
}
return cursor;
};
var obj = { a: { b: '1', c: '2' } }
find(obj, "a.b"); // 1
var set = function (root, path, value) {
var segments = path.split('.'),
cursor = root,
target;
for (var i = 0; i < segments.length - 1; ++i) {
cursor = cursor[segments[i]] || { };
}
cursor[segments[segments.length - 1]] = value;
};
set(obj, "a.k", function () { console.log("hello world"); });
find(obj, "a.k")(); // hello world
Вот мои 10 центов на случай, ниже функция будет получать / устанавливать на основе предоставленного пути,.. конечно, вы можете улучшить это, удалите || и замените его наObject.hasOwnProperty
если вы ошибочно заботитесь о ложных значениях,
я тестировал это с a.b.c
и ab2.c {a:{b:[0,1,{c:7}]}}
и он работает как для установки, так и для получения:).
ура
function helper(obj, path, setValue){
const l = String(path).split('.');
return l.reduce((o,i, idx)=>{
if( l.length-idx===1) { o[i] = setValue || o[i];return setValue ? obj : o[i];}
o[i] = o[i] || {};
return o[i];
}, x)
}
GET / SET ответ, который также работает в реагировать родной (вы не можете назначить для Object.prototype
В настоящее время):
Object.defineProperty(Object.prototype, 'getNestedProp', {
value: function(desc) {
var obj = this;
var arr = desc.split(".");
while(arr.length && (obj = obj[arr.shift()]));
return obj;
},
enumerable: false
});
Object.defineProperty(Object.prototype, 'setNestedProp', {
value: function(desc, value) {
var obj = this;
var arr = desc.split(".");
var last = arr.pop();
while(arr.length && (obj = obj[arr.shift()]));
obj[last] = value;
},
enumerable: false
});
Использование:
var a = { values: [{ value: null }] };
var b = { one: { two: 'foo' } };
a.setNestedProp('values.0.value', b.getNestedProp('one.two'));
console.log(a.values[0].value); // foo
Несколько лет спустя я обнаружил, что это обрабатывает область и массив. например a['b']["c"].d.etc
function getScopedObj(scope, str) {
let obj=scope, arr;
try {
arr = str.split(/[\[\]\.]/) // split by [,],.
.filter(el => el) // filter out empty one
.map(el => el.replace(/^['"]+|['"]+$/g, '')); // remove string quotation
arr.forEach(el => obj = obj[el])
} catch(e) {
obj = undefined;
}
return obj;
}
window.a = {b: {c: {d: {etc: 'success'}}}}
getScopedObj(window, `a.b.c.d.etc`) // success
getScopedObj(window, `a['b']["c"].d.etc`) // success
getScopedObj(window, `a['INVALID']["c"].d.etc`) // undefined
Я скопировал следующее из ответа Рикардо Томази и изменил его, создав также подобъекты, которые еще не существуют по мере необходимости. Это немного менее эффективно (больше if
и создание пустых объектов), но должно быть довольно хорошо.
Кроме того, это позволит нам сделать Object.prop(obj, 'a.b', false)
где мы не могли раньше. К сожалению, это все еще не позволит нам назначить undefined
... Пока не знаю, как это сделать.
/**
* Object.prop()
*
* Allows dot-notation access to object properties for both getting and setting.
*
* @param {Object} obj The object we're getting from or setting
* @param {string} prop The dot-notated string defining the property location
* @param {mixed} val For setting only; the value to set
*/
Object.prop = function(obj, prop, val){
var props = prop.split('.'),
final = props.pop(),
p;
for (var i = 0; i < props.length; i++) {
p = props[i];
if (typeof obj[p] === 'undefined') {
// If we're setting
if (typeof val !== 'undefined') {
// If we're not at the end of the props, keep adding new empty objects
if (i != props.length)
obj[p] = {};
}
else
return undefined;
}
obj = obj[p]
}
return typeof val !== "undefined" ? (obj[final] = val) : obj[final]
}
Если вы хотите преобразовать нотацию строковой точки в объект , я сделал небольшой удобный помощник, который может превратить строку вроде
a.b.c.d
со стоимостью
e
с
dotPathToObject("a.b.c.d", "value")
возвращая это:
{
"a": {
"b": {
"c": {
"d": "value"
}
}
}
}
https://gist.github.com/ahallora/9731d73efb15bd3d3db647efa3389c12
Решение:
function deepFind(key, data){
return key.split('.').reduce((ob,i)=> ob?.[i], data)
}
Использование:
const obj = {
company: "Pet Shop",
person: {
name: "John"
},
animal: {
name: "Lucky"
}
}
const company = deepFind("company", obj)
const personName = deepFind("person.name", obj)
const animalName = deepFind("animal.name", obj)
Если вы хотите преобразовать любой объект, содержащий ключи точечной нотации, в массив этих версий ключей, вы можете использовать это.
Это преобразует что-то вроде
{
name: 'Andy',
brothers.0: 'Bob'
brothers.1: 'Steve'
brothers.2: 'Jack'
sisters.0: 'Sally'
}
в
{
name: 'Andy',
brothers: ['Bob', 'Steve', 'Jack']
sisters: ['Sally']
}
convertDotNotationToArray(objectWithDotNotation) {
Object.entries(objectWithDotNotation).forEach(([key, val]) => {
// Is the key of dot notation
if (key.includes('.')) {
const [name, index] = key.split('.');
// If you have not created an array version, create one
if (!objectWithDotNotation[name]) {
objectWithDotNotation[name] = new Array();
}
// Save the value in the newly created array at the specific index
objectWithDotNotation[name][index] = val;
// Delete the current dot notation key val
delete objectWithDotNotation[key];
}
});
}
Вот моя реализация
Реализация 1
Object.prototype.access = function() {
var ele = this[arguments[0]];
if(arguments.length === 1) return ele;
return ele.access.apply(ele, [].slice.call(arguments, 1));
}
Реализация 2 (использование массива Reduce вместо Slice)
Object.prototype.access = function() {
var self = this;
return [].reduce.call(arguments,function(prev,cur) {
return prev[cur];
}, self);
}
Примеры:
var myobj = {'a':{'b':{'c':{'d':'abcd','e':[11,22,33]}}}};
myobj.access('a','b','c'); // returns: {'d':'abcd', e:[0,1,2,3]}
myobj.a.b.access('c','d'); // returns: 'abcd'
myobj.access('a','b','c','e',0); // returns: 11
он также может обрабатывать объекты внутри массивов, как для
var myobj2 = {'a': {'b':[{'c':'ab0c'},{'d':'ab1d'}]}}
myobj2.access('a','b','1','d'); // returns: 'ab1d'
Вот мой код без использования eval
, Его тоже легко понять.
function value(obj, props) {
if (!props) return obj;
var propsArr = props.split('.');
var prop = propsArr.splice(0, 1);
return value(obj[prop], propsArr.join('.'));
}
var obj = { a: { b: '1', c: '2', d:{a:{b:'blah'}}}};
console.log(value(obj, 'a.d.a.b')); //returns blah
Я использовал этот код в своем проекте
const getValue = (obj, arrPath) => (
arrPath.reduce((x, y) => {
if (y in x) return x[y]
return {}
}, obj)
)
Использование:
const obj = { id: { user: { local: 104 } } }
const path = [ 'id', 'user', 'local' ]
getValue(obj, path) // return 104
Использование сканирования объектов кажется излишним, но вы можете просто сделать
const objectScan = require('object-scan');
const get = (obj, p) => objectScan([p], { abort: true, rtn: 'value' })(obj);
const obj = { a: { b: '1', c: '2' } };
console.log(get(obj, 'a.b'));
// => 1
console.log(get(obj, '*.c'));
// => 2
В ридми есть много более сложных примеров.
Для получателя
var something = eval("obj.a.b.etc");
Для сеттер
eval("obj.a.b.etc = 'Something'");
Это один из тех случаев, когда вы спрашиваете 10 разработчиков и получаете 10 ответов.
Ниже представлено мое [упрощенное] решение для OP с использованием динамического программирования.
Идея состоит в том, что вы передадите существующий объект DTO, который хотите ОБНОВИТЬ. Это делает метод наиболее полезным в случае, когда у вас есть форма с несколькими элементами ввода, для которых атрибуты имени установлены с синтаксисом с точкой (плавный).
Пример использования:
<input type="text" name="person.contact.firstName" />
Фрагмент кода: