给定一个JS对象
var obj = { a: { b: '1', c: '2' } }`
和一个字符串
"a.b"
如何将字符串转换为点符号,以便我可以去
var val = obj.a.b
如果字符串只是'a',我可以使用obj[a]
,但这更复杂。我想有一些简单的方法,但它目前无法逃脱。
答案 0 :(得分:352)
答案 1 :(得分:47)
答案 2 :(得分:16)
一个更复杂的递归示例。
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
答案 3 :(得分:8)
如果您希望多次取消引用相同的路径,为每个点表示法路径构建一个函数实际上具有best performance by far(在上面的注释中与James Wilkins相关的性能测试中进行了扩展)。
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级深度的动态定义路径。
当然,可以使用类似的方法来创建setter函数:
var setter = new Function("obj", "newval", "obj." + path + " = newval;");
setter(obj, "some new val");
答案 4 :(得分:7)
自原帖以来已有多年。 现在有一个很棒的图书馆名为' object-path'。 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");
适用于深层嵌套的属性和数组。
答案 5 :(得分:7)
你也可以使用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;
答案 6 :(得分:5)
其他建议有点神秘,所以我想我会做出贡献:
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] } }
答案 7 :(得分:4)
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所说,you should never use eval。
另外,JSFiddle。
答案 8 :(得分:4)
我已经通过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/
答案 9 :(得分:4)
请注意,如果您已使用Lodash,则可以使用property
或get
功能:
var obj = { a: { b: '1', c: '2' } };
_.property('a.b')(obj); // => 1
_.get(obj, 'a.b'); // => 1
下划线还具有property
功能,但它不支持点表示法。
答案 10 :(得分:3)
我建议拆分路径并迭代它并减少你拥有的对象。对于缺少的属性,此提案的默认值为。
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'));
答案 11 :(得分:3)
您可以使用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!
答案 12 :(得分:2)
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
答案 13 :(得分:2)
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
答案 14 :(得分:2)
您可以通过点符号获取对象成员的值,只需一行代码:
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语句的字符串。
在我们的例子中,我们利用字符串函数体来检索带点符号的对象成员。
希望它有所帮助。
答案 15 :(得分:1)
如果您想将字符串点符号转换为对象,我已经制作了一个方便的小帮手,它可以将像 a.b.c.d
这样的字符串转换为 {{1} } e
返回:
dotPathToObject("a.b.c.d", "value")
https://gist.github.com/ahallora/9731d73efb15bd3d3db647efa3389c12
答案 16 :(得分:1)
我从Ricardo Tomasi's answer复制了以下内容并进行了修改,以便根据需要创建不存在的子对象。效率稍低(更多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]
}
答案 17 :(得分:0)
这是我提出的扩展解决方案: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)
}
答案 18 :(得分:0)
冒着击败死马的风险...... 我发现这在遍历嵌套对象时最有用,可以引用相对于基础对象或具有相同结构的类似对象的位置。为此,这对嵌套对象遍历函数很有用。请注意,我使用了一个数组来保存路径。修改它以使用字符串路径或数组将是微不足道的。另请注意,您可以为该值指定“undefined”,这与其他一些实现不同。
/*
* 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);
}
答案 19 :(得分:0)
我在我的项目中使用了这段代码
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
答案 20 :(得分:0)
这是我的实施
实施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'
答案 21 :(得分:0)
如果您希望将包含点符号键的任何对象转换为这些键的阵列版本,则可以使用它。
这将转换类似
ffmpeg -i input-hev1.mp4 -c:v copy -tag:v hvc1 -bsf:v hevc_mp4toannexb
-c:a copy output-hvc1.mov
到
{
name: 'Andy',
brothers.0: 'Bob'
brothers.1: 'Steve'
brothers.2: 'Jack'
sisters.0: 'Sally'
}
{
name: 'Andy',
brothers: ['Bob', 'Steve', 'Jack']
sisters: ['Sally']
}
答案 22 :(得分:0)
是的,它在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.Example:
{a:{b:11}}.getNestedProperty('a.b'); //returns 11
UPD 2.下一个扩展程序在我的项目中破坏了mongoose。我也读过它可能会破坏jquery。所以,永远不要以下一个方式去做
Object.prototype.getNestedProperty = function (propertyName) {
var result = this;
var arr = propertyName.split(".");
while (arr.length && result) {
result = result[arr.shift()];
}
return result;
};
答案 23 :(得分:0)
使用object-scan似乎有点矫kill过正,但是您可以轻松做到
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
自述文件中有很多更高级的示例。
答案 24 :(得分:0)
如果您想以最快的方式执行此操作,同时处理路径解析或属性解析的任何问题,请查看 path-value。
const {resolveValue} = require('path-value');
const value = resolveValue(obj, 'a.b.c');
该库是 100% TypeScript,适用于 NodeJS + 所有网络浏览器。而且它是完全可扩展的,您可以使用较低级别的 resolvePath
,并根据需要以自己的方式处理错误。
const {resolvePath} = require('path-value');
const res = resolvePath(obj, 'a.b.c'); //=> low-level parsing result descriptor
答案 25 :(得分:0)
这是我的代码,不使用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
答案 26 :(得分:0)
这是其中一种情况,您询问 10 个开发人员,您会得到 10 个答案。
以下是我使用动态编程的 OP [简化] 解决方案。
这个想法是您将传递一个您希望更新的现有 DTO 对象。这使得该方法在您的表单包含多个输入元素的表单中最有用,这些输入元素的名称属性使用点(连贯)语法设置。
示例使用:
<input type="text" name="person.contact.firstName" />
代码片段:
const setFluently = (obj, path, value) => {
if (typeof path === "string") {
return setFluently(obj, path.split("."), value);
}
if (path.length <= 1) {
obj[path[0]] = value;
return obj;
}
const key = path[0];
obj[key] = setFluently(obj[key] ? obj[key] : {}, path.slice(1), value);
return obj;
};
const origObj = {
a: {
b: "1",
c: "2"
}
};
setFluently(origObj, "a.b", "3");
setFluently(origObj, "a.c", "4");
console.log(JSON.stringify(origObj, null, 3));
答案 27 :(得分:0)
function at(obj, path, val = undefined) {
// If path is an Array,
if (Array.isArray(path)) {
// it returns the mapped array for each result of the path
return path.map((path) => at(obj, path, val));
}
// Uniting several RegExps into one
const rx = new RegExp(
[
/(?:^(?:\.\s*)?([_a-zA-Z][_a-zA-Z0-9]*))/,
/(?:^\[\s*(\d+)\s*\])/,
/(?:^\[\s*'([^']*(?:\\'[^']*)*)'\s*\])/,
/(?:^\[\s*"([^"]*(?:\\"[^"]*)*)"\s*\])/,
/(?:^\[\s*`([^`]*(?:\\`[^`]*)*)`\s*\])/,
]
.map((r) => r.source)
.join("|")
);
let rm;
while (rm = rx.exec(path.trim())) {
// Matched resource
let [rf, rp] = rm.filter(Boolean);
// If no one matches found,
if (!rm[1] && !rm[2]) {
// it will replace escape-chars
rp = rp.replace(/\\(.)/g, "$1");
}
// If the new value is set,
if ("undefined" != typeof val && path.length == rf.length) {
// assign a value to the object property and return it
return (obj[rp] = val);
}
// Going one step deeper
obj = obj[rp];
// Removing a step from the path
path = path.substr(rf.length).trim();
}
if (path) {
throw new SyntaxError();
}
return obj;
}
// Test object schema
let o = { a: { b: [ [ { c: { d: { '"e"': { f: { g: "xxx" } } } } } ] ] } };
// Print source object
console.log(JSON.stringify(o));
// Set value
console.log(at(o, '.a["b"][0][0].c[`d`]["\\"e\\""][\'f\']["g"]', "zzz"));
// Get value
console.log(at(o, '.a["b"][0][0].c[`d`]["\\"e\\""][\'f\']["g"]'));
// Print result object
console.log(JSON.stringify(o));
答案 28 :(得分:-1)
目前尚不清楚你的问题是什么。鉴于你的对象,obj.a.b
会给你“2”。如果你想操纵字符串使用括号,你可以这样做:
var s = 'a.b';
s = 'obj["' + s.replace(/\./g, '"]["') + '"]';
alert(s); // displays obj["a"]["b"]
答案 29 :(得分:-1)
这是我要付的10美分,波纹管功能将根据提供的路径进行获取/设置。
确保您可以改善它,删除||如果您确实错误地考虑了错误的值,则将其替换为Object.hasOwnProperty
我用a.b.c
和a.b.2.c {a:{b:[0,1,{c:7}]}}
对其进行了测试,并且可以同时进行设置和获取:)。
cheerz
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)
}