8wDlpd.png
8wDFp9.png
8wDEOx.png
8wDMfH.png
8wDKte.png

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

anthony sottile 2月前

169 0

我有一个这样的数据结构:var someObject = {'part1': {'name':'第 1 部分','size':'20','qty':'50'},'part2': {'name':'第 2 部分',...

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

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 来实现这一点?

帖子版权声明 1、本帖标题:通过字符串路径访问嵌套的 JavaScript 对象和数组
    本站网址:http://xjnalaquan.com/
2、本网站的资源部分来源于网络,如有侵权,请联系站长进行删除处理。
3、会员发帖仅代表会员个人观点,并不代表本站赞同其观点和对其真实性负责。
4、本站一律禁止以任何方式发布或转载任何违法的相关信息,访客发现请向站长举报
5、站长邮箱:yeweds@126.com 除非注明,本帖由anthony sottile在本站《object》版块原创发布, 转载请注明出处!
最新回复 (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"]));
  • 根据之前的回答,我创建了一个可以处理括号的函数。但由于拆分,括号内没有点。

    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);
    }
    
  • 最近刚遇到同样的问题并成功使用了 https://npmjs.org/package/tea-properties ,它也 set 嵌套了对象/数组:

    得到:

    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
    
  • 抱歉,我没有提到 someObject 尚未初始化。至于原因,someObject 是通过 Web 服务获取的。我想要一个由 part1name、part2qty 等组成的标头数组。这样我就可以循环遍历标头数组并根据 part1name 值作为 someObject 的“键”/路径获取我想要的值。

  • @Komaruloh:我认为你会写当你创建变量时对象尚未初始化。顺便说一句,我不明白为什么你不能在适当的时间进行赋值?

  • 很有趣。但就我而言,当我将值赋给 part1name 时,someObject 尚未初始化。我只知道结构。这就是我使用字符串来描述结构的原因。并希望能够使用它从 someObject 查询我的数据。谢谢分享您的想法。:)

  • 如果您需要在编码时访问不同的嵌套键而又不知道它(解决它们很简单),您可以使用数组符号访问器:

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

    函数更短,更清晰,解释器会为您检查语法错误等等。

    顺便说一句,我觉得在适当的时候进行简单的任务就足够了……

  • DotObject = obj => new Proxy(obj, {
      get: function(o,k) {
        const m = k.match(/(.+?)\.(.+)/)
        return m ? this.get(o[m[1]], m[2]) : o[k]
      }
    })
    
    const test = DotObject({a: {b: {c: 'wow'}}})
    console.log(test['a.b.c'])
  • 可以通过将逻辑拆分为三个独立的函数来简化此操作:

    const isVal = a => a != null; // everything except undefined + null
    
    const prop = prop => obj => {
        if (isVal(obj)) {
            const value = obj[prop];
            if (isVal(value)) return value;
            else return undefined;
        } else return undefined;
    };
    
    const path = paths => obj => {
        const pathList = typeof paths === 'string' ? paths.split('.') : paths;
        return pathList.reduce((value, key) => prop(key)(value), obj);
    };
    
    //usage:
    const myObject = { foo: { bar: { baz: 'taco' } } };
    const result = path('foo.bar')(myObject);
    //results => { baz: 'taco' }
    

    此变体支持:

    • 传递数组或字符串参数
    • 在调用和执行期间 undefined 处理
    • 独立测试每个功能
    • 独立使用每个功能
  • 根据 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'))
  • 您可以使用 ramda 图书馆。

    学习 ramda 还可以帮助您轻松处理不可变对象。

    
    var obj = {
      a:{
        b: {
          c:[100,101,{
            d: 1000
          }]
        }
      }
    };
    
    
    var lens = R.lensPath('a.b.c.2.d'.split('.'));
    var result = R.view(lens, obj);
    
    
    

    https://codepen.io/ghominejad/pen/BayJZOQ

  • 我正在使用 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;
    };
    
  • @CarlesAlcolea 默认情况下,js 不会检查对象的键是否存在:如果对象中没有属性 b,abc 将引发异常。如果您需要某种方式默默地忽略错误的键路径(我不推荐这样做),您仍然可以用这个 keys.forEach((key)=> obj = (obj||{})[key]); 替换 forEach

  • 虽然 reduce 很好,但我很惊讶没有人使用 forEach:

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

    测试

  • 引用 16

    有一个 npm 模块可以执行此操作: https://github.com/erictrinh/safe-access

    使用示例:

    var access = require('safe-access');
    access(very, 'nested.property.and.array[0]');
    
  • 简单的函数,允许使用字符串或数组路径。

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

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

  • /**
     * 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")
    
  • 如果你想要一个能够正确检测并报告路径解析问题细节的解决方案,我为此编写了自己的解决方案 - 库 path-value .

    const {resolveValue} = require('path-value');
    
    resolveValue(someObject, 'part1.name'); //=> Part 1
    resolveValue(someObject, 'part2.qty'); //=> 50
    resolveValue(someObject, 'part3.0.name'); //=> Part 3A
    

    请注意,对于索引我们使用 .0 ,而不是 [0] ,因为解析后者会增加性能损失,而 .0 直接在 JavaScript 中运行,因此速度非常快。

    但是,完整的 ES5 JavaScript 语法也受支持,只是需要先进行标记:

    const {resolveValue, tokenizePath} = require('path-value');
    
    const path = tokenizePath('part3[0].name'); //=> ['part3', '0', 'name']
    
    resolveValue(someObject, path); //=> Part 3A
    
返回
作者最近主题: