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

675

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

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


不确定你在这里问什么?你想查询part1.name并返回文本"part1.name"吗?还是你想要一种方法来获取存储在part1.name中的值? - BonyT
你尝试过像这样做吗:var part1name = someObject.part1name; - Rafay
1
@BonyT:我想查询someObject.part1.name并返回它的值(“Part 1”)。但是,我希望查询(我称之为“键”)存储在变量'part1name'中。感谢您的回复。@3nigma:我当然可以这样做。但这不是我的意图。感谢回复。 - Komaruloh
1
在重复的答案中,我喜欢fyr的回答。https://dev59.com/Gmoy5IYBdhLWcg3wCpoz - Steve Black
显示剩余2条评论
46个回答

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

2

2

基于Alnitak的答案

我加入了一个检查来包装填充,并将函数简化为单个链接的减少。

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


2

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


2
这可以通过将逻辑分为三个单独的函数来简化:
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
  • 独立测试每个函数
  • 独立使用每个函数

2
你可以使用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


1
如果您需要在编码时访问不同的嵌套键,但又不知道它们具体是什么(这将很容易地解决),您可以使用数组符号访问器:
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
抱歉没有提到someObject尚未初始化的事情。原因是,someObject是通过Web服务获取的。我想要一个标题数组,其中包含part1name、part2qty等部分。这样,我就可以循环遍历标题数组,并根据part1name值作为“键”/路径来获取所需的值。 - Komaruloh

1

根据之前的回答,我创建了一个函数,可以处理括号,但由于分割,不能处理其中的点。

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

1

我的解决方案基于@AdrianoSpadoni提供的方案,旨在满足克隆对象的需求。

function generateData(object: any, path: string, value: any): object {
  const clone = JSON.parse(JSON.stringify(object));
  path
    .split(".")
    .reduce(
    (o, p, i) => (o[p] = path.split(".").length === ++i ? value : o[p] || {}),
  clone
);
  return clone;
}

1

扩展Mohamad Hamouday的答案将填补缺失的键

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

        let level = 0;
        var Return_Value;
        Path.reduce((a, b)=>{
            console.log(level,':',a, '|||',b)
            if (!strict){
              if (!(b in a)) 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;
    }
}


例子

obja = {
  "a": {
    "b":"nom"
  }
}

// Set
path = "c.b" // Path does not exist
Object_Manager(obja,path.split('.'), 'test_new_val', 'Set', false);

// Expected Output: Object { a: Object { b: "nom" }, c: Object { b: "test_new_value" } }


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接