通过字符串路径访问嵌套的JavaScript对象和Aray


454

我有一个像这样的数据结构:

var someObject = {
    'part1' : {
        'name': 'Part 1',
        'size': '20',
        'qty' : '50'
    },
    'part2' : {
        'name': 'Part 2',
        'size': '15',
        'qty' : '60'
    },
    'part3' : [
        {
            'name': 'Part 3A',
            'size': '10',
            'qty' : '20'
        }, {
            'name': 'Part 3B',
            'size': '5',
            'qty' : '20'
        }, {
            'name': 'Part 3C',
            'size': '7.5',
            'qty' : '20'
        }
    ]
};

我想使用这些变量访问数据:

var part1name = "part1.name";
var part2quantity = "part2.qty";
var part3name1 = "part3[0].name";

part1name应该用someObject.part1.name的值(即“第1部分”)填充。part2quantity填充60。

无论如何,可以使用纯JavaScript或JQuery来实现这一点?


不知道您在这里问什么?您希望能够查询part1.name并返回文本“ part1.name”吗?还是您想要一种方法来获取存储在part1.name中的值?
BonyT 2011年

您是否尝试过像var part1name = someObject.part1name;`
Rafay11年

1
@BonyT:我想查询someObject.part1.name并返回它的值(“第1部分”)。但是,我希望查询(我称其为“键”)存储在变量“ part1name”中。感谢您的回复。@ 3nigma:我当然可以。但这不是我的意图。谢谢回复。
Komaruloh 2011年

1
在重复的答案,我爱FYR的答案stackoverflow.com/questions/8817394/...
史蒂夫布莱克

Answers:


520

我只是根据已经拥有的一些类似代码制作的,它似乎可以工作:

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无效索引。


19
这很漂亮。请通过将其包装为节点包将其贡献给Internet。
t3dodson

14
@ t3dodson我刚刚做过:github.com/capaj/object-resolve-path只是要注意,当您的属性名称本身包含'[]'时,这不能很好地发挥作用。正则表达式将其替换为“。” 它并没有按预期的方式工作
Capaj

20
好东西; 使用lodash库,还可以执行以下操作:_.get(object, nestedPropertyString);
ian

17
这可能会在评论的海洋中迷失,但是,如果您尝试解决不存在的属性,则会出错。所以'part3[0].name.iDontExist'。添加检查以查看是否o是对象,可以if in解决此问题。(如何处理取决于您自己)。请参阅更新的提琴:jsfiddle.net/hEsys/418
ste2425

2
这真是金子。我们有一个基于配置的应用程序,这很有帮助!谢谢!
Christian Esperar,2016年

182

这是我使用的解决方案:

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

局限性:

  • []数组索引不能使用方括号(),尽管在分隔符(例如.)之间指定数组索引可以正常工作,如上所示。

7
使用reduce是一个很好的解决方案(也可以_.reduce()从下划线或lodash库中使用)
Alp

3
我认为self这里可能未定义。你是说this
白金天蓝色

2
这是我通过路径设置值的补充:pastebin.com/jDp5sKT9
mroach

1
有人知道如何将其移植到TypeScript吗?
亚当·普洛彻

1
@ SC1000好主意。在大多数浏览器中都提供默认参数之前就写了这个答案。我将其更新为“ function resolve(path,obj = self)”,因为有意将全局对象作为默认值引用。
speigg

179

现在,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'

9
这应该是唯一被接受的答案,因为这是唯一适用于点和括号语法的方法,并且当路径中的键字符串中包含“ []”时,它不会失败。
卡帕杰

7
这个。另外,它还支持_.set(...)
Josh C.

如果找不到对象,该怎么办?
DDave

@DDave如果作为对象传递的值未定义或不是对象,_.get则将显示与在提供的对象中未找到键时相同的行为。例如_.get(null, "foo") -> undefined_.get(null, "foo", "bar") -> "bar"。但是,此行为未在文档中定义,因此可能会发生更改。
伊恩·沃克-史柏柏

5
@Capaj你在开玩笑吗?谁不想/不能使用lodash?
安德烈·菲格雷多

74

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

对于现成的功能,该功能还可以识别假数字,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,i) => o[p] = path.split('.').length === ++i ? 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

2
我喜欢这种技术。这确实很乱,但是我想使用这种技术进行分配。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

1
我喜欢使用的想法,但降低你的逻辑似乎关闭0undefinednull值。 {a:{b:{c:0}}}返回null而不是0。也许显式检查null或undefined将清除这些问题。 (p,c)=>p === undefined || p === null ? undefined : p[c]
SmujMaiku '17

@SmujMaiku,您好,“准备使用”函数正确返回了“ 0”,“未定义”和“空”,我刚刚在控制台上进行了测试:resolvePath({a:{b:{c:0}}},' abc',null)=> 0; 它检查键是否存在,而不是值本身,从而避免了多次检查
Adriano Spadoni

在这里defaultValue无效,但是使用Reflect.has(o, k) ? ...(ES6 Reflect.has)可以正常工作
Andre Figueiredo

setPath将在最后一个选择器的首次出现时设置值。例如let o = {}; setPath(o, 'a.a', 0)结果{a: 0}而不是{a: {a: 0}}。看到这篇文章的解决方案。
pkfm

62

您必须自己解析字符串:

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

它使解析更加容易。

演示


@Felix Kling:您的解决方案确实为我提供了我所需要的。我为此深表谢意。但是Alnitak也提供了不同的方法,并且似乎也可以工作。由于我只能选择一个答案,因此我将选择Alnitak答案。并不是说他的解决方案比您或类似的东西更好。无论如何,我非常感谢您的解决方案和您付出的努力。
Komaruloh 2011年

1
@Komaruloh:哦,我认为您可以随时对自己的问题投票表决……。无论如何我还是在开玩笑,我不需要更多的声誉;)编码愉快!
Felix Kling

1
@Felix Kling:您至少需要15个声誉才能获得投票。:)我相信69k +并不需要更多的声誉。谢谢
Komaruloh

@Felix FWIW-从[]语法转换为属性语法非常简单。
Alnitak

4
如果将while循环更改为,while (l > 0 && (obj = obj[current]) && i < l)则此代码也适用于不带点的字符串。
Snea 2014年

39

也适用于对象中的数组/数组。防御无效值。

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


10
谢谢,这是最好,最高效的
Dominic

@无尽,我想强调路径应该用点分隔项目。大括号不起作用。即访问数组中的第一项使用“ 0.sp ace”。
TheZver

26

使用eval:

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

换行返回错误时未定义

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

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

运用评估的力量时,请运用常识并当心。这有点像一把军刀,如果打开它,则有90%的机会切断肢体。它不适合所有人。


7
评估eval是否是一个好主意,取决于属性字符串数据来自何处。我怀疑您是否有理由担心黑客通过静态“ var p ='abc'; eval(p);”入侵 输入电话。这是一个非常好的主意。
詹姆斯·威尔金斯

17

您可以使用简单的以下技巧,在没有任何外部JavaScript库的情况下,通过点符号来获取深层对象成员的值:

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

在你的情况下获得的价值part1.namesomeObject刚做:

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

这是一个简单的小提琴演示:https : //jsfiddle.net/harishanchu/oq5esowf/


3
function deep_value(obj,path){return new Function('o','return o。'+ path)(obj); }
ArcangelZith's

14

这可能永远都不会出现……但是无论如何,这里还是存在。

  1. 更换 []括号语法为.
  2. .字符拆分
  3. 删除空白字符串
  4. 找到路径(否则undefined

// "one liner" (ES6)

const deep_value = (obj, path) => 
  path
    .replace(/\[|\]\.?/g, '.')
    .split('.')
    .filter(s => s)
    .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


11

这是一个带有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);

普伦克


7

我想您是在要求:

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

我认为OP正在寻求最后的解决方案。但是,字符串没有Split方法,而是split
2011年

实际上我在问最后一个。partName变量由字符串填充,该字符串指示值的键结构。您的解决方案似乎很有意义。但是,我可能需要修改数据的扩展深度,例如4-5级或更高级别。我想知道是否可以用它统一处理数组和对象?
Komaruloh 2011年

7

在这里,我提供了更多方法,这些方法在许多方面似乎更快:

选项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):低级字符扫描(无正则表达式/分割/等,仅是快速字符扫描)。 注意:这不支持索引引号。

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”,因为有充分的理由使用它。另外,“它用于Doug Crockford的JSON解析器中。” 如果输入是安全的,则完全没有问题。使用正确的工具完成正确的工作,就是这样。


6

AngularJS

Speigg的方法非常简洁明了,尽管我在搜索通过字符串路径访问AngularJS $ scope属性的解决方案时发现了此答复,并且做了一些修改后就可以了:

$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')

有关使用AngularJS$scope.$eval的另一种方法,请参见AngularJS。
georgeawg

5

为了以防万一,有人在2017年或以后访问此问题并寻找一种易于记忆的方式,这是一篇有关在JavaScript中访问嵌套对象的精心撰写的博客文章,

无法读取未定义错误的属性“ foo”

使用Array Reduce访问嵌套对象

让我们来看这个示例结构

const user = {
    id: 101,
    email: 'jack@dev.com',
    personalInfo: {
        name: 'Jack',
        address: [{
            line1: 'westwish st',
            line2: 'washmasher',
            city: 'wallas',
            state: 'WX'
        }]
    }
}

为了能够访问嵌套数组,您可以编写自己的数组reduce util。

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;

免责声明:我是该软件包的作者。


3

我还没有找到可以使用字符串路径进行所有操作的程序包,因此我最终编写了自己的快速小程序包,该程序包支持insert(),get()(默认返回),set()和remove()。 )操作。

您可以使用点表示法,方括号,数字索引,字符串数字属性和带有非单词字符的键。下面的简单用法:

> var jsocrud = require('jsocrud');

...

// Get (Read) ---
> var obj = {
>     foo: [
>         {
>             'key w/ non-word chars': 'bar'
>         }
>     ]
> };
undefined

> jsocrud.get(obj, '.foo[0]["key w/ non-word chars"]');
'bar'

https://www.npmjs.com/package/jsocrud

https://github.com/vertical-knowledge/jsocrud


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

3

功能简单,允许使用字符串或数组路径。

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

如果您要发布代码作为答案,请解释代码为何回答问题。
Tieson T. 16/12/26


2

虽然reduce很好,但我感到惊讶的是没有人用过:

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

测试


您甚至都没有检查obj [key]是否确实存在。这是不可靠的。
卡尔斯·阿尔科利亚

@CarlesAlcolea默认情况下,js都不检查对象的键是否存在:a.b.c如果b对象中没有属性,则会引发异常。如果您需要以某种方式静默消除错误的密钥路径(我不建议这样做),您仍然可以用此keys.forEach((key)=> obj = (obj||{})[key]);
密钥

我通过它运行了一个缺少花括号的对象,这很糟糕:)
Carles Alcolea

1

最近才有同样的问题,并成功使用了https://npmjs.org/package/tea-properties,它也set嵌套了object / arrays:

得到:

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

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

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

组:

var o = {};

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

assert(o.prop.arr[0].foo, 'bar'); // true

“此模块已停产。请使用chaijs / pathval。”
Patrick Fisher

1

受到@webjay的答案的启发:https://stackoverflow.com/a/46008856/4110122

我做了这个功能,您可以使用它来获取/设置/取消设置对象中的任何值

function Object_Manager(obj, Path, value, Action) 
{
    try
    {
        if(Array.isArray(Path) == false)
        {
            Path = [Path];
        }

        let level = 0;
        var Return_Value;
        Path.reduce((a, b)=>{
            level++;
            if (level === Path.length)
            {
                if(Action === 'Set')
                {
                    a[b] = value;
                    return value;
                }
                else if(Action === 'Get')
                {
                    Return_Value = a[b];
                }
                else if(Action === 'Unset')
                {
                    delete a[b];
                }
            } 
            else 
            {
                return a[b];
            }
        }, obj);
        return Return_Value;
    }

    catch(err)
    {
        console.error(err);
        return obj;
    }
}

要使用它:

 // Set
 Object_Manager(Obj,[Level1,Level2,Level3],New_Value, 'Set');

 // Get
 Object_Manager(Obj,[Level1,Level2,Level3],'', 'Get');

 // Unset
 Object_Manager(Obj,[Level1,Level2,Level3],'', 'Unset');

1

我正在用React开发在线商店。我试图更改复制状态对象中的值,以在提交时更新其原始状态。上面的示例对我没有用,因为它们大多数会使复制对象的结构发生变化。我找到了用于访问和更改深层嵌套对象属性值的函数的工作示例:https : //lowrey.me/create-an-object-by-path-in-javascript-2/这是:

const createPath = (obj, path, value = null) => {
  path = typeof path === 'string' ? path.split('.') : path;
  let current = obj;
  while (path.length > 1) {
    const [head, ...tail] = path;
    path = tail;
    if (current[head] === undefined) {
      current[head] = {};
    }
    current = current[head];
  }
  current[path[0]] = value;
  return obj;
};

1

基于Alnitak的答案

我将polyfill包裹在一张支票中,并将功能简化为单链式简化。

if (Object.byPath === undefined) {
  Object.byPath = (obj, path) => path
    .replace(/\[(\w+)\]/g, '.$1')
    .replace(/^\./, '')
    .split(/\./g)
    .reduce((ref, key) => key in ref ? ref[key] : ref, obj)
}

const data = {
  foo: {
    bar: [{
      baz: 1
    }]
  }
}

console.log(Object.byPath(data, 'foo.bar[0].baz'))


0

如果您需要在编码时不知道其他嵌套键的情况下访问它(解决这些问题很简单),则可以使用数组符号访问器:

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

它们等效于点表示法访问器,并且在运行时可能会有所不同,例如:

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

var part1name = someObject[part][property];

相当于

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

要么

var part1name = someObject.part1.name;

我希望这能解决您的问题...

编辑

我不会使用字符串来维护某种xpath 查询来访问对象值。由于您必须调用函数来解析查询并检索值,因此我将遵循另一条路径(不是:

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

// usage: part1name.apply(someObject);

或者,如果您对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);

这些功能更短,更清晰,解释器会为您检查语法错误等。

顺便说一句,我觉得在正确的时间完成一个简单的任务就足够了。


有趣。但是在我的情况下,当我将值分配给part1name时,someObject尚未初始化。我只知道结构。这就是为什么我使用字符串来描述结构。并希望能够使用它从someObject查询我的数据。感谢您分享您的想法。:)
Komaruloh

@Komaruloh:我认为您会在创建变量时写道该对象尚未初始化。顺便说一句,我不明白,你为什么不能在适当的时候做作业?
Eineki 2011年

很抱歉没有提及someObject尚未初始化。由于这个原因,someObject是通过Web服务获取的。我想要一个包含part1name,part2qty等组成的标头数组。这样我就可以遍历标头数组,并根据part1name值获取想要的值作为someObject的“键” /路径。
Komaruloh

0

这里的解决方案仅用于访问深层嵌套的键。我需要一个用于访问,添加,修改和删除密钥的密钥。这是我想出的:

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

建立在Alnitak的答案之上:

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

}

这也允许您设置一个值!

我创建了一个NPM包github上有这个问题,以及


0

代替字符串,可以使用数组处理嵌套的对象和数组,例如: ["my_field", "another_field", 0, "last_field", 10]

这是一个基于此数组表示形式更改字段的示例。我在react.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

根据先前的答案,我创建了一个也可以处理方括号的函数。但是由于分裂,它们内部没有点。

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

// (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

有工作Underscoreproperty还是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>

祝好运...

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.