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

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的值填充,即“Part 1”。part2quantity也是一样,它的容量是60。

有没有办法实现这与纯javascript或JQuery?


当前回答

我正在用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;
};

其他回答

我还没有找到一个包来使用字符串路径执行所有操作,所以我最终编写了自己的快速小包,它支持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

这是我使用的解决方案:

function resolve(path, obj=self, separator='.') {
    var properties = Array.isArray(path) ? path : path.split(separator)
    return properties.reduce((prev, curr) => 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

限制:

不能将方括号([])用于数组下标-尽管在分隔符令牌之间指定数组下标(例如,.)可以正常工作,如上所示。

基于前面的回答,我创建了一个也可以处理括号的函数。但由于裂开,里面没有点。

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

简单的函数,允许字符串或数组路径。

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

OR

console.log(get(obj, ['a', 'b', 'c'])); //foo

使用对象扫描,这就变成了一行。然而,更重要的是,这个解决方案考虑性能:

在搜索期间遍历一次输入(即使查询了多个键) 解析只在init上发生一次(如果查询多个对象) 允许使用*进行扩展语法

// const objectScan = require('object-scan'); const 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' }] }; const get = (haystack, needle) => objectScan([needle], { rtn: 'value', abort: true })(haystack); console.log(get(someObject, 'part1.name')); // => Part 1 console.log(get(someObject, 'part2.qty')); // => 60 console.log(get(someObject, 'part3[0].name')); // => Part 3A const getAll = (haystack, ...needles) => objectScan(needles, { reverse: false, rtn: 'entry', joined: true })(haystack); console.log(getAll(someObject, 'part1.name', 'part2.qty', 'part3[0].name')); /* => [ [ 'part1.name', 'Part 1' ], [ 'part2.qty', '60' ], [ 'part3[0].name', 'Part 3A' ] ] */ console.log(getAll(someObject, 'part1.*')); /* => [ [ 'part1.name', 'Part 1' ], [ 'part1.size', '20' ], [ 'part1.qty', '50' ] ] */ .as-console-wrapper {max-height: 100% !important; top: 0} <script src="https://bundle.run/object-scan@13.8.0"></script>

声明:我是object-scan的作者