简体   繁体   English

查找嵌套对象中所有匹配键值的路径

[英]Find path to ALL matching key values in nested object

I have a function that gets me the path to the first finding of a nested object where the key and the value matches.我有一个函数可以让我找到第一个找到键和值匹配的嵌套对象的路径。

function getPath(obj, givenKey, givenValue) {
    for(var key in obj) {                                   
        if(obj[key] && typeof obj[key] === "object") {      
            var result = getPath(obj[key], givenValue, givenKey);
            if(result) {                                    
                result.unshift(key)
                return result;                              
            }
        } else if(obj[key] === givenValue && key === givenKey ) {
            return [key];
        }   
    }
}

sample data样本数据

var myObj = [
{
    "name": "needle",
    "children": [
    {
        "name": "group2",
        "children": [
        {
            "name": "item0"
        }]
    }]
},
{
    "name": "item1"
},
{
    "name": "needleGroup",
    "children": [
    {
        "name": "needleNestedGroup",
        "children": [
        {
            "name": "item3"
        },
        {
            "name": "needleNestedDeeperGroup",
            "children": [
            {
                "name": "needle"
            }]
        }]
    }]
}];

expected output预期产出

getPath(myObj, "name", "needle"):
      [0, "name"]
      ["2","children","0","children","1","children","0","name"]

However, I have now an object that contains these key-values multiple times, so I have multiple matches.但是,我现在有一个多次包含这些键值的对象,所以我有多个匹配项。

How can I get all of them in an array?我怎样才能把它们全部放在一个数组中? My current function is just stopping after it finds the first match.我当前的功能在找到第一个匹配项后就停止了。 The fact, that it's recursive makes things very complicated for me事实上,它是递归的,这让我觉得事情很复杂

Instead of returning the value, you could push it to an array and keep iterating.您可以将其推送到数组并继续迭代,而不是返回该值。

At the end of the function, you return the array.在函数结束时, return数组。

 function getPath(obj, givenKey, givenValue) { let matches = []; for (var key in obj) { if (obj[key] && typeof obj[key] === "object") { var result = getPath(obj[key], givenValue, givenKey); if (result) { result.unshift(key) matches.push(...result); } } else if (obj[key] === givenValue && key === givenKey) { matches.push(key); } } return matches; }

I would write this atop a more generic findAllPaths function that accepts a predicate and finds the paths of all nodes in the object that match that predicate.我会把它写在一个更通用的findAllPaths函数之上,它接受一个谓词并找到对象中与该谓词匹配的所有节点的路径。 With that, then findPathsByName is as simple as (target) => findAllPaths (({name}) => name == target) .这样, findPathsByName就像(target) => findAllPaths (({name}) => name == target)

In turn, I build findAllPaths on pathEntries , variants of which I use all the time .反过来,我在findAllPaths上构建了pathEntries ,我一直使用它的变体。 This function turns an object into an array of path/value pairs.此函数将对象转换为路径/值对数组。 Some versions only generate the leaf nodes.有些版本只生成叶节点。 This one generate it for all nodes, including the root (with an empty path.) The basic idea of this function is to turn something like:这个函数为所有节点生成它,包括根节点(带有一个空路径)。这个函数的基本思想是变成这样的:

{a: 'foo', b: {c: ['bar', 'baz'], f: 'qux'}}

into this:进入这个:

[
  [[], {a: 'foo', b: {c: ['bar', 'baz'], f: 'qux'}}], 
  [['a'], 'foo'], 
  [['b'], {c: ['bar', 'baz'], f: 'qux'}], 
  [['b', 'c'], ['bar', 'baz']], 
  [['b', 'c', 0], 'bar'], 
  [['b', 'c', 1], 'baz'], 
  [['b', 'f'], 'qux']
]

where the first item in every subarray is a path and the second a reference to the value at that path.其中每个子数组中的第一项是路径,第二项是对该路径值的引用。

Here is what it might look like:这是它可能的样子:

 const pathEntries = (obj) => [ [[], obj], ...Object (obj) === obj ? Object .entries (obj) .flatMap ( ([k, x]) => pathEntries (x) .map ( ([p, v]) => [[Array .isArray (obj) ? Number (k) : k, ... p], v] ) ) : [] ] const findAllPaths = (predicate) => (o) => [...pathEntries (o)] .filter (([p, v]) => predicate (v, p)) .map (([p]) => p) const findPathsByName = (target) => findAllPaths (({name}) => name == target) const myObj = [{name: "needle", children: [{name: "group2", children: [{name: "item0"}]}]}, {name: "item1"}, {name: "needleGroup", children: [{name: "needleNestedGroup", children: [{name: "item3"}, {name: "needleNestedDeeperGroup", children: [{name: "needle"}]}]}]}] console .log (findPathsByName ('needle') (myObj))
 .as-console-wrapper {max-height: 100% !important; top: 0}

The question asked for string values for the array indices.该问题要求提供数组索引的字符串值。 I prefer the integer values myself as done here, but you simplify the function a bit:我更喜欢自己在这里完成的整数值,但是您可以稍微简化一下函数:

-          ([p, v]) => [[Array .isArray (obj) ? Number (k) : k, ... p], v]
+          ([p, v]) => [[k, ... p], v]

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM