英文:
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;
}
英文:
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 = () => (o) =>
p == undefined ? o : getPath(ps) (o && o
)
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))
// or
console .log ('Full-path:', getPath (['tabs-3', 'Collection B', 'Level 2', 'Data A'])(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 'tabs-3'
property of your data. The second one shows my preferred way, using 'tabs-3'
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] === "**")
yield *loop(t, path.slice(1), match, true)
else if (Object(t) === t)
for (const k of Object.keys(t))
if (path[0] === "*" || 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, ["a","*","x"]))
console.log(
JSON.stringify(path),
JSON.stringify(value)
)
["a","b","x"] {"y":1,"z":2}
["a","c","x"] {"y":4}
["a","d","x"] 6
搜索以z
结尾的任何路径 -
for (const [path, value] of get(data, ["**","z"]))
console.log(
JSON.stringify(path),
JSON.stringify(value)
)
["a","b","x","z"] 2
["a","b","y","z"] 3
["a","c","y","z"] 5
["b","c","z"] 7
获取完整路径a
,c
,x
,y
-
for (const [path, value] of get(data, ["a","c","x","y"]))
console.log(
JSON.stringify(path),
JSON.stringify(value)
)
["a","c","x","y"] 4
创建复杂的模式 -
for (const [path, value] of get(data, ["*","b","**","y"]))
console.log(
JSON.stringify(path),
JSON.stringify(value)
)
["a","b","x","y"] 1
["a","b","y"] {"z":3}
获取不存在的路径 -
for (const [path, value] of get(data, ["x","y","z"]))
console.log(
JSON.stringify(path),
JSON.stringify(value)
)
// <empty result>
英文:
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] === "**")
yield *loop(t, path.slice(1), match, true)
else if (Object(t) === t)
for (const k of Object.keys(t))
if (path[0] === "*" || 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, ["a","*","x"]))
console.log(
JSON.stringify(path),
JSON.stringify(value)
)
["a","b","x"] {"y":1,"z":2}
["a","c","x"] {"y":4}
["a","d","x"] 6
Search for any path ending in z
-
for (const [path, value] of get(data, ["**","z"]))
console.log(
JSON.stringify(path),
JSON.stringify(value)
)
["a","b","x","z"] 2
["a","b","y","z"] 3
["a","c","y","z"] 5
["b","c","z"] 7
Get full path a
, c
, x
, y
-
for (const [path, value] of get(data, ["a","c","x","y"]))
console.log(
JSON.stringify(path),
JSON.stringify(value)
)
["a","c","x","y"] 4
Make complex patterns -
for (const [path, value] of get(data, ["*","b","**","y"]))
console.log(
JSON.stringify(path),
JSON.stringify(value)
)
["a","b","x","y"] 1
["a","b","y"] {"z":3}
Get non-existent path -
for (const [path, value] of get(data, ["x","y","z"]))
console.log(
JSON.stringify(path),
JSON.stringify(value)
)
// <empty result>
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] === "**")
yield *loop(t, path.slice(1), match, true)
else if (Object(t) === t)
for (const k of Object.keys(t))
if (path[0] === "*" || 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, ["a","*","x"]))
console.log(
JSON.stringify(path),
JSON.stringify(value)
)
for (const [path, value] of get(data, ["**","z"]))
console.log(
JSON.stringify(path),
JSON.stringify(value)
)
for (const [path, value] of get(data, ["a","c","x","y"]))
console.log(
JSON.stringify(path),
JSON.stringify(value)
)
for (const [path, value] of get(data, ["x","y","z"]))
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) =>
path.length === 0
? [[match, t]]
: path[0] === "**"
? loop(t, path.slice (1), match, true)
: Object(t) === t
? Object.keys(t).flatMap(k =>
path[0] === "*" || 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)
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论