问题
I have similiary problem like here: JavaScript: Find all parents for element in tree recursive
But I don't find path by name but by direct path.
const path = ["name1", "name4", "name5"];
const data = [
{
'name': 'name1',
'tree': [
{'name': 'name2'},
{'name': 'name3'},
{
'name': 'name4',
'tree': [
{'name': 'name5'},
{'name': 'name6'}
]
},
{'name': 'name7'}
]
},
{
'name': 'name8',
'tree': [
{'name': 'name9'}
]
}
];
It returns every possible path or nothing.
When path is too short, it returns nothing.
When path is too long, it returns nothing.
Thanks for help!
Examples of desired output:
const path = ["name1", "name4", "name5"];
findAPath(data, path)
Returns: ["name1", "name4", "name5"]
const path = ["name1", "name7", "name5"];
findAPath(data, path)
Returns []
const path = ["name1", "name4", "name5", "name5"];
findAPath(data, path)
Returns []
My trying:
let index = 0;
function find(data, index) {
let index = index;
data.some((o) => {
if(o.name == path[index]) {
index++;
find(o.tree, index);
}
});
// I don't know what return here.
// I need to probably return path where I am.
return <>;
}
回答1:
using Array.prototype.flatMap
Here's a functional solution using a mutual recursion technique -
const None =
Symbol ()
const findPath = (tree = [], names = [], r = []) =>
tree.length && names.length // base: and
? tree.flatMap(branch => findPath1(branch, names, r))
: tree.length || names.length // inductive: xor
? []
: [ r ] // inductive: nor // inductive: nor
const findPath1 = ({ name = "", tree = [] } = {}, [ q = None, ...more ] = [], r = []) =>
name === "" && q === None // base: and
? [ r ]
: name === "" || q === None || name !== q // inductive: xor
? []
: findPath(tree, more, [ ...r, q ]) // inductive: nor
findPath(data, ["name1", "name4", "name5"])
// => [ [ "name1", "name4", "name5" ] ]
NB if your data contains multiple paths to the input values, all paths will be returned -
const data = [
{
'name': 'name1', // name1
'tree': [
{'name': 'name2'},
{'name': 'name3'},
{
'name': 'name4', // name1->name4
'tree': [
{'name': 'name5'}, // name1->name4->name5
{'name': 'name6'}
]
},
{
'name': 'name4', // name1->name4
'tree': [
{'name': 'name5'}, // name1->name4->name5
{'name': 'name6'}
]
},
{'name': 'name7'}
]
},
{
'name': 'name8',
'tree': [
{'name': 'name9'}
]
}
]
Just like you asked, it returns every possible path, or nothing -
findPath(data, ["name1", "name4", "name5"])
// => [ [ "name1", "name4", "name5" ],
// [ "name1", "name4", "name5" ] ]
findPath(data, [ "name1", "name7" ])
// => [ [ "name1", "name7" ] ]
findPath(data, [ "name1", "name9" ])
// => []
When a path is too short or too long, it will return nothing -
findPath(data, [ "name1", "name4" ])
// => []
findPath(data, [ "name1", "name4", "name5", "name6" ])
// => []
Expand the snippet below to verify the results in your own browser -
const None =
Symbol ()
const findPath = (tree = [], names = [], r = []) =>
tree.length && names.length
? tree.flatMap(branch => findPath1(branch, names, r))
: tree.length || names.length
? []
: [ r ]
const findPath1 = ({ name = "", tree = [] } = {}, [ q = None, ...more ] = [], r = []) =>
name === "" && q === None
? [ r ]
: name === "" || q === None || name !== q
? []
: findPath(tree, more, [ ...r, q ])
const data = [
{
'name': 'name1',
'tree': [
{'name': 'name2'},
{'name': 'name3'},
{
'name': 'name4',
'tree': [
{'name': 'name5'},
{'name': 'name6'}
]
},
{'name': 'name7'}
]
},
{
'name': 'name8',
'tree': [
{'name': 'name9'}
]
}
]
console.log(findPath(data, ["name1", "name4", "name5"]))
// [ [ "name1", "name4", "name5" ] ]
console.log(findPath(data, [ "name1", "name7" ]))
// [ [ "name1", "name7" ] ]
console.log(findPath(data, [ "name1", "name9" ]))
// []
using Generators
Here's an alternative implementation using generators -
const None =
Symbol ()
const findPath = function* (tree = [], names = [], r = [])
{ if (tree.length && names.length) // base: and
for (const branch of tree)
yield* findPath1(branch, names, r)
else if (tree.length || names.length) // inductive: xor
return
else // inductive: nor
yield r
}
const findPath1 = function* ({ name = "", tree = [] } = {}, [ q = None, ...more ] = [], r = [])
{ if (name === "" && q === None) // base: and
yield r
else if (name === "" || q === None || name !== q) // inductive: xor
return
else // inductive: nor
yield* findPath(tree, more, [ ...r, q ])
}
It has the exact same output as above, only to coerce the iterable generator into an array, we use Array.from -
Array.from(findPath(data, ["name1", "name4", "name5"]))
// => [ [ "name1", "name4", "name5" ] ]
Array.from(findPath(data, [ "name1", "name7" ]))
// => [ [ "name1", "name7" ] ]
Array.from(findPath(data, [ "name1", "name9" ]))
// => []
Expand the snippet below to verify the results in your own browser -
const None =
Symbol ()
const findPath = function* (tree = [], names = [], r = [])
{ if (tree.length && names.length)
for (const branch of tree)
yield* findPath1(branch, names, r)
else if (tree.length || names.length)
return
else
yield r
}
const findPath1 = function* ({ name = "", tree = [] } = {}, [ q = None, ...more ] = [], r = [])
{ if (name === "" && q === None)
yield r
else if (name === "" || q === None || name !== q)
return
else
yield* findPath(tree, more, [ ...r, q ])
}
const data = [
{
'name': 'name1',
'tree': [
{'name': 'name2'},
{'name': 'name3'},
{
'name': 'name4',
'tree': [
{'name': 'name5'},
{'name': 'name6'}
]
},
{'name': 'name7'}
]
},
{
'name': 'name8',
'tree': [
{'name': 'name9'}
]
}
]
console.log(Array.from(findPath(data, ["name1", "name4", "name5"])))
// [ [ "name1", "name4", "name5" ] ]
console.log(Array.from(findPath(data, [ "name1", "name7" ])))
// [ [ "name1", "name7" ] ]
console.log(Array.from(findPath(data, [ "name1", "name9" ])))
// []
how they're the same; how they're not
Note the similarity between the two implementations and how the result is formed. Both use mutual recursion. The functional solution uses expressions whereas the generator solution uses statements. The generator implementation extends a distinct advantage where by we can chose to stop or continue iteration ("finding") whenever we want.
For example, imagine an input where there are ten (10) unique paths for the given input. Perhaps we want to just return the first match,
const findFirst = (tree = [], names = []) =>
{ for (const path of findPath(tree, names))
return path
}
Or get the first three (3) matches -
const findFirst3 = (tree = [], names = []) =>
{ const r = []
for (const path of findPath(tree, names))
if (r.length < 3)
r.push(path)
return r
}
Or get the first N -
const findFirstN = (tree = [], names = [], n = 0) =>
{ const r = []
for (const path of findPath(tree, names))
if (r.length < n)
r.push(path)
return r
}
Generators are flexible like this. By contrast, the flatMap implementation is eager and always returns all results.
来源:https://stackoverflow.com/questions/57680671/find-path-in-tree-by-direct-path