给定一个JS对象
var obj = { a: { b: '1', c: '2' } }
和一个字符串
"a.b"
如何将字符串转换为点表示法,以便我可以
var val = obj.a.b
如果字符串是正义的'a'
,我可以使用obj[a]
。但这更复杂。我想有一种简单的方法,但是目前可以逃脱。
给定一个JS对象
var obj = { a: { b: '1', c: '2' } }
和一个字符串
"a.b"
如何将字符串转换为点表示法,以便我可以
var val = obj.a.b
如果字符串是正义的'a'
,我可以使用obj[a]
。但这更复杂。我想有一种简单的方法,但是目前可以逃脱。
Answers:
最近的说明:虽然我很高兴这个答案获得了很多好评,但我还是有些恐惧。如果需要将点符号字符串(例如“ xabc”)转换为引用,则可能(可能)是表明发生了非常错误的迹象(除非您正在执行一些奇怪的反序列化)。
也就是说,找到答案的新手必须问自己一个问题:“我为什么要这样做?”
如果您的用例很小并且不会遇到性能问题,那么这样做通常是可以的,并且您将不需要在抽象上进行构建,以后再使其变得更加复杂。实际上,如果这将减少代码复杂性并使事情简单,那么您可能应该继续执行OP要求的操作。但是,如果不是这种情况,请考虑以下任何一项是否适用:
情况1:作为处理数据的主要方法(例如,作为应用程序传递对象并取消引用对象的默认形式)。就像问“如何从字符串中查找函数或变量名”一样。
- 这是不好的编程习惯(特别是不必要的元编程,并且这违反了函数的无副作用编码风格,并且会影响性能)。在这种情况下发现自己的新手,应该考虑使用数组表示形式,例如['x','a','b','c'],或者如果可能的话甚至使用更直接/简单/简单的方法:例如不丢失首先跟踪引用本身(如果仅是客户端或服务器端,则是最理想的选择),等等。(预先存在的唯一ID很难添加,但如果规范另外要求它可以使用无论存在。)
情况2:使用序列化数据或将显示给用户的数据。就像使用日期作为字符串“ 1999-12-30”而不是使用Date对象一样(如果不小心,可能会导致时区错误或增加序列化复杂性)。或者你知道自己在做什么。
- 这也许很好。请注意,没有点串“。” 在您清理过的输入片段中。
如果您发现自己一直在使用此答案并在字符串和数组之间来回转换,则可能情况很糟糕,应考虑使用另一种方法。
这是一个优雅的单缸套,比其他解决方案短十倍:
function index(obj,i) {return obj[i]}
'a.b.etc'.split('.').reduce(index, obj)
[edit]或在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编译器所做的优化,您可能需要确保没有通过常规方法在每次调用时都重新定义任何嵌套函数(将它们放在闭包,对象或全局名称空间中)。
编辑:
要在评论中回答一个有趣的问题:
您还将如何将其变成二传手?不仅通过路径返回值,而且如果将新值发送到函数中,还要设置它们?–斯瓦德6月28日21:42
(旁注:可悲的是无法返回带有Setter的对象,因为这将违反调用约定;注释者似乎是指具有副作用的通用setter样式函数,如index(obj,"a.b.etc", value)
doing 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数组(本身就是Object的实例)特别鼓励使用整数键,即使您可以执行类似的操作x=[]; x["puppy"]=5;
。
但通常(并且有例外)x["somestring"]===x.somestring
(在允许时;您不能这样做x.123
)。
(请记住,如果使用的JS编译器可以证明不会违反规范,则可能会选择将其编译为更精巧的表示形式。)
因此,问题的答案取决于您是否假设这些对象仅接受整数(由于问题域中的限制)。让我们假设不是。那么有效的表达式是基本标识符加上一些.identifier
s加上一些["stringindex"]
s 的串联
那将等于a["b"][4]["c"]["d"][1][2][3]
,尽管我们可能也应该支持a.b["c\"validjsstringliteral"][3]
。您必须检查有关字符串文字的ecmascript语法部分,以了解如何解析有效的字符串文字。从技术上讲,您还需要检查(与我的第一个答案不同)a
有效的javascript标识符。
但是,如果您的字符串中不包含逗号或方括号,那么一个简单的答案就是匹配长度为1+的字符序列(不在集合,
or [
或or中)]
:
> "abc[4].c.def[1][2][\"gh\"]".match(/[^\]\[.]+/g)
// ^^^ ^ ^ ^^^ ^ ^ ^^^^^
["abc", "4", "c", "def", "1", "2", ""gh""]
如果您的字符串不包含转义字符或"
character,并且由于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年特别版
让我们全力以赴,并为语法纯洁的 f谐性做一个我们可以想出的最低效,可怕的,过度编程的解决方案。使用ES6代理对象!...我们还要定义一些属性(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是:{“ 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']为obj ['a'] ['b'] ['c']为:4
效率低下的想法:您可以根据输入参数修改以上内容以进行分派;要么使用.match(/[^\]\[.]+/g)
方法来支持obj['keys'].like[3]['this']
,要么如果instanceof Array
接受,那么就接受Array作为输入keys = ['a','b','c']; obj.H[keys]
。
根据建议,您可能想以“更软”的NaN风格处理未定义的索引(例如,index({a:{b:{c:...}}}, 'a.x.c')
返回未定义而不是未捕获的TypeError)...:
1)从一维索引情况({})['eg'] == undefined中“应该返回未定义而不是抛出错误”的角度来看,这是有意义的,因此“我们应该返回未定义而不是抛出错误。错误”。
2)从我们所做的角度来看,这是没有意义的,x['a']['x']['c']
在上面的示例中,它会因TypeError而失败。
就是说,您可以通过以下任一方法来替换约简函数,从而完成这项工作:
(o,i)=>o===undefined?undefined:o[i]
或
(o,i)=>(o||{})[i]
。
(您可以通过使用for循环并在未定义要进入下一个索引的子结果时中断/返回来提高效率,或者如果您希望这种失败很少发生,可以使用try-catch来提高效率。)
reduce
当前所有使用的浏览器都不支持。
Array.reduce
是ECMA-262标准的一部分。如果您确实希望支持过时的浏览器,则可以Array.prototype.reduce
在某个地方(例如developer.mozilla.org/en/JavaScript/Reference/Global_Objects/…)中定义示例实现。
var setget = function( obj, path ){ function index( robj,i ) {return robj[i]}; return path.split('.').reduce( index, obj ); }
递归涉及更多示例。
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
您也可以使用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;
如果您希望多次取消对同一路径的引用,那么为每个点表示法路径构建一个函数实际上实际上具有最佳的性能(扩展在上面的评论中链接到的James Wilkins 性能测试)。
var path = 'a.b.x';
var getter = new Function("obj", "return obj." + path + ";");
getter(obj);
就安全性和最坏情况的性能而言,使用Function构造函数具有与eval()相同的缺点,但是IMO对于需要极端动态性和高性能相结合的情况而言,是一个使用不足的工具。我使用这种方法来构建数组过滤器函数,并在AngularJS摘要循环中调用它们。我的配置文件始终显示Array.filter()步骤使用不到3毫秒的时间来解引用和过滤约2000个复杂对象,并使用3-4级深度的动态定义路径。
当然,可以使用类似的方法来创建setter函数:
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");
并且适用于深层嵌套的属性和数组。
我建议拆分路径并对其进行迭代,并减少所拥有的对象。该建议使用缺少属性的默认值。
const getValue = (object, keys) => keys.split('.').reduce((o, k) => (o || {})[k], object);
console.log(getValue({ a: { b: '1', c: '2' } }, 'a.b'));
console.log(getValue({ a: { b: '1', c: '2' } }, 'foo.bar.baz'));
其他建议有些含糊,所以我想我会做出贡献:
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!
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
方法相比,这是很多代码,但是就像Simon Willison所说的那样,您永远不要使用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/
[]
符号总是用于数组。可能也有以这种方式表示的对象键,例如,obj['some-problem/name'].list[1]
要解决此问题,我必须更新这样的arr_deref
功能 javascript function arr_deref(o, ref, i) { return !ref ? o : (o[(ref.slice(0, i ? -1 : ref.length)).replace(/^['"]|['"]$/g, '')]); }
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
您可以使用单行代码通过点表示法获取对象成员的值:
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对象。使用Function构造函数显式创建函数的语法是:
new Function ([arg1[, arg2[, ...argN]],] functionBody)
其中arguments(arg1 to argN)
必须是与有效javaScript标识符相对应的字符串,并且functionBody
是包含包含函数定义的javaScript语句的字符串。
在我们的例子中,我们利用字符串函数体的优势来检索带点表示法的对象成员。
希望能帮助到你。
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
我从里卡多·托马西(Ricardo Tomasi)的答案中复制了以下内容,并进行了修改,以创建必要时尚不存在的子对象。它的效率较低(更多的if
s和创建空对象),但是应该非常好。
而且,它使我们能够做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]
}
如果要将任何包含点符号键的对象转换为这些键的数组版本,可以使用此方法。
这将转换类似
{
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];
}
});
}
这是我的代码,不使用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
是的,是4年前问过的,是的,扩展基本原型通常不是一个好主意,但是,如果将所有扩展都放在一个位置,它们可能会很有用。
因此,这是我的方法。
Object.defineProperty(Object.prototype, "getNestedProperty", {
value : function (propertyName) {
var result = this;
var arr = propertyName.split(".");
while (arr.length && result) {
result = result[arr.shift()];
}
return result;
},
enumerable: false
});
现在,您可以在任何地方获取嵌套属性,而无需导入具有功能的模块或复制/粘贴功能。
UPD示例:
{a:{b:11}}.getNestedProperty('a.b'); //returns 11
UPD2。下一个扩展程序在我的项目中打破了猫鼬。我也读过它可能会破坏jQuery。所以,千万不要以其他方式
Object.prototype.getNestedProperty = function (propertyName) {
var result = this;
var arr = propertyName.split(".");
while (arr.length && result) {
result = result[arr.shift()];
}
return result;
};
这是我的实现
实施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(使用数组缩减而不是切片)
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'
这是我提出的扩展解决方案:ninjagecko
对我来说,简单的字符串表示法是不够的,因此以下版本支持以下内容:
index(obj,'data.accounts [0] .address [0] .postcode');
/**
* Get object by index
* @supported
* - arrays supported
* - array indexes supported
* @not-supported
* - multiple arrays
* @issues:
* index(myAccount, 'accounts[0].address[0].id') - works fine
* index(myAccount, 'accounts[].address[0].id') - doesnt work
* @Example:
* index(obj, 'data.accounts[].id') => returns array of id's
* index(obj, 'data.accounts[0].id') => returns id of 0 element from array
* index(obj, 'data.accounts[0].addresses.list[0].id') => error
* @param obj
* @param path
* @returns {any}
*/
var index = function(obj, path, isArray?, arrIndex?){
// is an array
if(typeof isArray === 'undefined') isArray = false;
// array index,
// if null, will take all indexes
if(typeof arrIndex === 'undefined') arrIndex = null;
var _arrIndex = null;
var reduceArrayTag = function(i, subArrIndex){
return i.replace(/(\[)([\d]{0,})(\])/, (i) => {
var tmp = i.match(/(\[)([\d]{0,})(\])/);
isArray = true;
if(subArrIndex){
_arrIndex = (tmp[2] !== '') ? tmp[2] : null;
}else{
arrIndex = (tmp[2] !== '') ? tmp[2] : null;
}
return '';
});
}
function byIndex(obj, i) {
// if is an array
if(isArray){
isArray = false;
i = reduceArrayTag(i, true);
// if array index is null,
// return an array of with values from every index
if(!arrIndex){
var arrValues = [];
_.forEach(obj, (el) => {
arrValues.push(index(el, i, isArray, arrIndex));
})
return arrValues;
}
// if array index is specified
var value = obj[arrIndex][i];
if(isArray){
arrIndex = _arrIndex;
}else{
arrIndex = null;
}
return value;
}else{
// remove [] from notation,
// if [] has been removed, check the index of array
i = reduceArrayTag(i, false);
return obj[i]
}
}
// reduce with byIndex method
return path.split('.').reduce(byIndex, obj)
}
冒着击败一头死马的风险...我发现这在遍历嵌套对象以参考您相对于基础对象或具有相同结构的相似对象所处的位置时最有用。为此,这对于嵌套对象遍历函数很有用。请注意,我使用了一个数组来保存路径。修改它以使用字符串路径或数组将是微不足道的。还要注意,与某些其他实现不同,您可以为该值分配“未定义”。
/*
* Traverse each key in a nested object and call fn(curObject, key, value, baseObject, path)
* on each. The path is an array of the keys required to get to curObject from
* baseObject using objectPath(). If the call to fn() returns falsey, objects below
* curObject are not traversed. Should be called as objectTaverse(baseObject, fn).
* The third and fourth arguments are only used by recursion.
*/
function objectTraverse (o, fn, base, path) {
path = path || [];
base = base || o;
Object.keys(o).forEach(function (key) {
if (fn(o, key, o[key], base, path) && jQuery.isPlainObject(o[key])) {
path.push(key);
objectTraverse(o[key], fn, base, path);
path.pop();
}
});
}
/*
* Get/set a nested key in an object. Path is an array of the keys to reference each level
* of nesting. If value is provided, the nested key is set.
* The value of the nested key is returned.
*/
function objectPath (o, path, value) {
var last = path.pop();
while (path.length && o) {
o = o[path.shift()];
}
if (arguments.length < 3) {
return (o? o[last] : o);
}
return (o[last] = value);
}
我在项目中使用了这段代码
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
几年后,我发现它可以处理范围和数组。例如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
目前尚不清楚您的问题是什么。给定您的对象,obj.a.b
原样会给您“ 2”。如果要操纵字符串以使用方括号,则可以执行以下操作:
var s = 'a.b';
s = 'obj["' + s.replace(/\./g, '"]["') + '"]';
alert(s); // displays obj["a"]["b"]
a.b.c
,也无法真正实现他们想要的目标。他们想要的是价值,而不是eval
道路。
a.b.c
,但您是对的,显然他想获取/设置属性的值为obj.a.b
。这个问题令我感到困惑,因为他说他想“转换字符串” ..
这是我要付的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)
}
eval
是邪恶的;不要使用它