返回与特定分支遍历匹配的对象。

huangapple go评论67阅读模式
英文:

Return object that matches specific branch traversal

问题

这个问题起源于这里

给定此数据:

{
    "tabs-3": {
        "Collection A": {
            "Level 2": {
                "Data A": {
                    "tab3graph25": {
                        "30/04": 21750,
                        "31/03": 19428,
                        "29/05": 20955
                    }
                }
            }
        },
        "Collection B": {
            "Level 2": {
                "Data A": {
                    "tab3graph33": {
                        "30/04": 56863,
                        "31/03": 62298,
                        "29/05": 56044
                    }
                }
            }
        },
        "Collection C": {
            "Level 2": {
                "Data A": {
                    "tab3graph40": {
                        "30/04": 56044,
                        "31/03": 62298,
                        "29/05": 56863
                    }
                }
            }
        }
    }
}

函数的形式是这样的,它将以对象和搜索列表作为输入,例如,var searchList = [Collection B, Level 2, Data A],然后返回tab3graph33:{...values...}

初始函数可参考如下:

function searchObjectForValues(obj, searchList) {
    var matchingObjects = {};

    function recursiveSearch(currentObj, path) {
        if (typeof currentObj === 'object' && currentObj !== null) {
            for (var key in currentObj) {
                if (currentObj.hasOwnProperty(key)) {
                    var value = currentObj[key];
                    var currentPath = path.concat(key);

                    if (searchList.includes(key)) {
                        matchingObjects[key] = currentObj[key];
                    }

                    recursiveSearch(value, currentPath);
                }
            }
        }
    }

    recursiveSearch(obj, []);

    return matchingObjects;
}
英文:

This question started here

The conversation exceeded the original scope, so asking the expanded question here.

Given this data:

{
"tabs-3": {
    "Collection A": {
        "Level 2": {
            "Data A": {
                "tab3graph25": {
                    "30/04": 21750,
                    "31/03": 19428,
                    "29/05": 20955
                }
            }
        }
    }
    "Collection B": {
        "Level 2": {
            "Data A": {
                "tab3graph33": {
                    "30/04": 56863,
                    "31/03": 62298,
                    "29/05": 56044
                }
            }
        }
    }
    "Collection C": {
        "Level 2": {
            "Data A": {
                "tab3graph40": {
                    "30/04": 56044,
                    "31/03": 62298,
                    "29/05": 56863
                }
            }
        }
    }
}

}

What would a function look like that would take as it's input the holding object to search, in this case, "tabs-3", and a key set to look for, e.g., var searchList = [Collection B, Level 2, Data A] and then return "tab3graph33:{...values...}

The original starting function I have is here for reference ,

function searchObjectForValues(obj, searchList) {
  var matchingObjects = {};

  function recursiveSearch(currentObj, path) {
    if (typeof currentObj === 'object' && currentObj !== null) {
      for (var key in currentObj) {
        if (currentObj.hasOwnProperty(key)) {
          var value = currentObj[key];
          var currentPath = path.concat(key);

          if (searchList.includes(key)) {
            matchingObjects[key] = currentObj[key];
          }

          recursiveSearch(value, currentPath);
        }
      }
    }
  }

  recursiveSearch(obj, []);

  return matchingObjects;
}

Although the answers in the original question are much more elegant than mine.

答案1

得分: 1

我们可以为此编写一个相当简单的递归函数。通常,实用程序库中已经包含了这样一个函数,如果您已经在使用类似 Underscore 或 Ramda 的东西的话。

const getPath = ([p, ...ps]) => (o) =>
  p == undefined ? o : getPath(ps)(o && o[p])

const data = {
  "tabs-3": {
    "Collection A": {
      "Level 2": {
        "Data A": {
          tab3graph25: {
            "30/04": 21750,
            "31/03": 19428,
            "29/05": 20955
          }
        }
      },
      "Collection B": {
        "Level 2": {
          "Data A": {
            tab3graph33: {
              "30/04": 56863,
              "31/03": 62298,
              "29/05": 56044
            }
          }
        }
      },
      "Collection C": {
        "Level 2": {
          "Data A": {
            tab3graph40: {
              "30/04": 56044,
              "31/03": 62298,
              "29/05": 56863
            }
          }
        }
      }
    }
  }
}

const tabs3 = data['tabs-3']
console.log('Partial path:', getPath(['Collection B', 'Level 2', 'Data A'])(tabs3))

// 或者
console.log('Full-path:', getPath(['tabs-3', 'Collection B', 'Level 2', 'Data A'])(data))

第一个 console.log 显示了您可能希望使用的方式,其中引用了数据中 'tabs-3' 属性中的对象。第二个示例展示了我更喜欢的方式,将 'tabs-3' 作为属性列表中的一个属性。

英文:

We can write a fairly simple recursive function for this. Utility libraries usually include one, too, if you're already using something like Underscore or Ramda.

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

const getPath = (

) =&gt; (o) =&gt; p == undefined ? o : getPath(ps) (o &amp;&amp; o

) const data = {&quot;tabs-3&quot;: {&quot;Collection A&quot;: {&quot;Level 2&quot;: {&quot;Data A&quot;: {tab3graph25: {&quot;30/04&quot;: 21750, &quot;31/03&quot;: 19428, &quot;29/05&quot;: 20955}}}}, &quot;Collection B&quot;: {&quot;Level 2&quot;: {&quot;Data A&quot;: {tab3graph33: {&quot;30/04&quot;: 56863, &quot;31/03&quot;: 62298, &quot;29/05&quot;: 56044}}}}, &quot;Collection C&quot;: {&quot;Level 2&quot;: {&quot;Data A&quot;: {tab3graph40: {&quot;30/04&quot;: 56044, &quot;31/03&quot;: 62298, &quot;29/05&quot;: 56863}}}}}} const tabs3 = data[&#39;tabs-3&#39;] console .log (&#39;Partial path:&#39;, getPath ([&#39;Collection B&#39;, &#39;Level 2&#39;, &#39;Data A&#39;])(tabs3)) // or console .log (&#39;Full-path:&#39;, getPath ([&#39;tabs-3&#39;, &#39;Collection B&#39;, &#39;Level 2&#39;, &#39;Data A&#39;])(data))

<!-- language: lang-css -->

.as-console-wrapper {max-height: 100% !important; top: 0}

<!-- end snippet -->

The first console.log shows how you would use this the way you seem to want, with a reference to the object in the &#39;tabs-3&#39; property of your data. The second one shows my preferred way, using &#39;tabs-3&#39; as one more property in the list.

答案2

得分: 1

以下是您请求的翻译部分:

这是一个有趣的get函数,它接受***通配符。给定这个data -

const data = {
  a: {
    b: {
      x: {y:1, z:2},
      y: {z:3},
    },
    c: {
      x: {y:4},
      y: {z:5},
    },
    d: {x:6},
  },
  b: {c: {z:7}}
}

我们可以将get写成一个递归生成器 -

function *get(t, path) {
  function *loop(t, path, match, wild) {
    if (path.length === 0)
      yield [match, t]
    else if (path[0] === &quot;**&quot;)
      yield *loop(t, path.slice(1), match, true)
    else if (Object(t) === t)
      for (const k of Object.keys(t))
        if (path[0] === &quot;*&quot; || path[0] === k)
          yield *loop(t[k], path.slice(1), [...match, k], false)
        else if (wild)
          yield *loop(t[k], path, [...match, k], true)
  }
  yield *loop(t, path, [], false)
}

搜索以a开头,然后跟一个通配符,然后是x的路径 -

for (const [path, value] of get(data, [&quot;a&quot;,&quot;*&quot;,&quot;x&quot;]))
  console.log(
    JSON.stringify(path),
    JSON.stringify(value)
  )
[&quot;a&quot;,&quot;b&quot;,&quot;x&quot;] {&quot;y&quot;:1,&quot;z&quot;:2}
[&quot;a&quot;,&quot;c&quot;,&quot;x&quot;] {&quot;y&quot;:4}
[&quot;a&quot;,&quot;d&quot;,&quot;x&quot;] 6

搜索以z结尾的任何路径 -

for (const [path, value] of get(data, [&quot;**&quot;,&quot;z&quot;]))
  console.log(
    JSON.stringify(path),
    JSON.stringify(value)
  )
[&quot;a&quot;,&quot;b&quot;,&quot;x&quot;,&quot;z&quot;] 2
[&quot;a&quot;,&quot;b&quot;,&quot;y&quot;,&quot;z&quot;] 3
[&quot;a&quot;,&quot;c&quot;,&quot;y&quot;,&quot;z&quot;] 5
[&quot;b&quot;,&quot;c&quot;,&quot;z&quot;] 7

获取完整路径acxy -

for (const [path, value] of get(data, [&quot;a&quot;,&quot;c&quot;,&quot;x&quot;,&quot;y&quot;]))
  console.log(
    JSON.stringify(path),
    JSON.stringify(value)
  )
[&quot;a&quot;,&quot;c&quot;,&quot;x&quot;,&quot;y&quot;] 4

创建复杂的模式 -

for (const [path, value] of get(data, [&quot;*&quot;,&quot;b&quot;,&quot;**&quot;,&quot;y&quot;]))
  console.log(
    JSON.stringify(path),
    JSON.stringify(value)
  )
[&quot;a&quot;,&quot;b&quot;,&quot;x&quot;,&quot;y&quot;] 1
[&quot;a&quot;,&quot;b&quot;,&quot;y&quot;] {&quot;z&quot;:3}

获取不存在的路径 -

for (const [path, value] of get(data, [&quot;x&quot;,&quot;y&quot;,&quot;z&quot;]))
  console.log(
    JSON.stringify(path),
    JSON.stringify(value)
  )
// &lt;empty result&gt;
英文:

Here's a fun get function which accepts * and ** wildcards. Given this data -

const data = {
  a: {
    b: {
      x: {y:1, z:2},
      y: {z:3},
    },
    c: {
      x: {y:4},
      y: {z:5},
    },
    d: {x:6},
  },
  b: {c: {z:7}}
}

We can write get as a recursive generator -

function *get(t, path) {
  function *loop(t, path, match, wild) {
    if (path.length === 0)
      yield [match, t]
    else if (path[0] === &quot;**&quot;)
      yield *loop(t, path.slice(1), match, true)
    else if (Object(t) === t)
      for (const k of Object.keys(t))
        if (path[0] === &quot;*&quot; || path[0] === k)
          yield *loop(t[k], path.slice(1), [...match, k], false)
        else if (wild)
          yield *loop(t[k], path, [...match, k], true)
  }
  yield *loop(t, path, [], false)
}

Search for a path starting with a, followed by one wildcard, followed by x -

for (const [path, value] of get(data, [&quot;a&quot;,&quot;*&quot;,&quot;x&quot;]))
  console.log(
    JSON.stringify(path),
    JSON.stringify(value)
  )
[&quot;a&quot;,&quot;b&quot;,&quot;x&quot;] {&quot;y&quot;:1,&quot;z&quot;:2}
[&quot;a&quot;,&quot;c&quot;,&quot;x&quot;] {&quot;y&quot;:4}
[&quot;a&quot;,&quot;d&quot;,&quot;x&quot;] 6

Search for any path ending in z -

for (const [path, value] of get(data, [&quot;**&quot;,&quot;z&quot;]))
  console.log(
    JSON.stringify(path),
    JSON.stringify(value)
  )
[&quot;a&quot;,&quot;b&quot;,&quot;x&quot;,&quot;z&quot;] 2
[&quot;a&quot;,&quot;b&quot;,&quot;y&quot;,&quot;z&quot;] 3
[&quot;a&quot;,&quot;c&quot;,&quot;y&quot;,&quot;z&quot;] 5
[&quot;b&quot;,&quot;c&quot;,&quot;z&quot;] 7

Get full path a, c, x, y -

for (const [path, value] of get(data, [&quot;a&quot;,&quot;c&quot;,&quot;x&quot;,&quot;y&quot;]))
  console.log(
    JSON.stringify(path),
    JSON.stringify(value)
  )
[&quot;a&quot;,&quot;c&quot;,&quot;x&quot;,&quot;y&quot;] 4

Make complex patterns -

for (const [path, value] of get(data, [&quot;*&quot;,&quot;b&quot;,&quot;**&quot;,&quot;y&quot;]))
  console.log(
    JSON.stringify(path),
    JSON.stringify(value)
  )
[&quot;a&quot;,&quot;b&quot;,&quot;x&quot;,&quot;y&quot;] 1
[&quot;a&quot;,&quot;b&quot;,&quot;y&quot;] {&quot;z&quot;:3}

Get non-existent path -

for (const [path, value] of get(data, [&quot;x&quot;,&quot;y&quot;,&quot;z&quot;]))
  console.log(
    JSON.stringify(path),
    JSON.stringify(value)
  )
// &lt;empty result&gt;

Verify the results below in your own browser -

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

function *get(t, path) {
  function *loop(t, path, match, wild) {
    if (path.length === 0)
      yield [match, t]
    else if (path[0] === &quot;**&quot;)
      yield *loop(t, path.slice(1), match, true)
    else if (Object(t) === t)
      for (const k of Object.keys(t))
        if (path[0] === &quot;*&quot; || path[0] === k)
          yield *loop(t[k], path.slice(1), [...match, k], false)
        else if (wild)
          yield *loop(t[k], path, [...match, k], true)
  }
  yield *loop(t, path, [], false)
}

const data = {
  a: {
    b: {
      x: {y:1, z:2},
      y: {z:3},
    },
    c: {
      x: {y:4},
      y: {z:5},
    },
    d: {x:6},
  },
  b: {c: {z:7}}
}

for (const [path, value] of get(data, [&quot;a&quot;,&quot;*&quot;,&quot;x&quot;]))
  console.log(
    JSON.stringify(path),
    JSON.stringify(value)
  )
  
for (const [path, value] of get(data, [&quot;**&quot;,&quot;z&quot;]))
  console.log(
    JSON.stringify(path),
    JSON.stringify(value)
  )
  
for (const [path, value] of get(data, [&quot;a&quot;,&quot;c&quot;,&quot;x&quot;,&quot;y&quot;]))
  console.log(
    JSON.stringify(path),
    JSON.stringify(value)
  )
  
for (const [path, value] of get(data, [&quot;x&quot;,&quot;y&quot;,&quot;z&quot;]))
  console.log(
    JSON.stringify(path),
    JSON.stringify(value)
  )

<!-- language: lang-css -->

.as-console-wrapper { min-height: 100%; top: 0; }

<!-- end snippet -->

sans generators

Here's the same solution without using generators. The interface is identical however it always returns an array instead of an iterator -

function get(t, path) {
  const loop = (t, path, match, wild) =&gt;
    path.length === 0
      ? [[match, t]]
  : path[0] === &quot;**&quot;
      ? loop(t, path.slice (1), match, true)
  : Object(t) === t
      ? Object.keys(t).flatMap(k =&gt;
          path[0] === &quot;*&quot; || path[0] === k
            ? loop(t[k], path.slice (1), [...match, k], false)
        : wild
            ? loop(t[k], path, [...match, k], true)
        : []
        )
  : []
  return loop(t, path, [], false)
}

huangapple
  • 本文由 发表于 2023年6月8日 23:00:02
  • 转载请务必保留本文链接:https://go.coder-hub.com/76433185.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定