我想解决的更大问题是,根据这些数据:
var data = [
{ id: 1 },
{ id: 2 },
{ id: 3 },
{ id: 4, children: [
{ id: 6 },
{ id: 7, children: [
{id: 8 },
{id: 9 }
]}
]},
{ id: 5 }
]
我想创建一个返回findById(data, id)
的函数{ id: id }
。例如,findById(data, 8)
应返回{ id: 8 }
,findById(data, 4)
应返回{ id: 4, children: [...] }
。
为了实现这一点,我递归地使用了Array.prototype.find
,但是当return
将对象混合在一起时遇到了麻烦。我的实现将路径返回给特定对象。
例如,当我使用findById(data, 8)
时,它会将路径返回到{ id: 8 }
:
{ id: 4, children: [ { id: 6 }, { id: 7, children: [ { id: 8}, { id: 9] } ] }
相反,我希望它只是返回
{ id: 8 }
var data = [
{ id: 1 },
{ id: 2 },
{ id: 3 },
{ id: 4, children: [
{ id: 6 },
{ id: 7, children: [
{id: 8 },
{id: 9 }
]}
]},
{ id: 5 }
]
function findById(arr, id) {
return arr.find(a => {
if (a.children && a.children.length > 0) {
return a.id === id ? true : findById(a.children, id)
} else {
return a.id === id
}
})
return a
}
console.log(findById(data, 8)) // Should return { id: 8 }
// Instead it returns the "path" block: (to reach 8, you go 4->7->8)
//
// { id: 4,
// children: [ { id: 6 }, { id: 7, children: [ {id: 8}, {id: 9] } ] }
答案 0 :(得分:14)
你所拥有的问题是发现的冒泡。如果在嵌套结构中找到id,则回调会尝试返回元素,该元素被解释为true,即find的值。
find
方法对数组中存在的每个元素执行一次回调函数,直到找到一个回调返回true值的元素。 [MDN]
如果找到,我会建议使用递归样式进行短路搜索。
var data = [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4, children: [{ id: 6 }, { id: 7, children: [{ id: 8 }, { id: 9 }] }] }, { id: 5 }];
function findById(data, id) {
function iter(a) {
if (a.id === id) {
result = a;
return true;
}
return Array.isArray(a.children) && a.children.some(iter);
}
var result;
data.some(iter);
return result
}
console.log(findById(data, 8));

答案 1 :(得分:3)
我只会使用常规循环和递归样式搜索:
function findById(data, id) {
for(var i = 0; i < data.length; i++) {
if (data[i].id === id) {
return data[i];
} else if (data[i].children && data[i].children.length && typeof data[i].children === "object") {
findById(data[i].children, id);
}
}
}
//findById(data, 4) => Object {id: 4, children: Array[2]}
//findById(data, 8) => Object {id: 8}
答案 2 :(得分:1)
让我们考虑基于递归调用的实现:
Dim Mypath As String, fileName As String, mps As Variant, mps_temp As String, mydate As Date, IntroDate As Date, i as integer
Application.ScreenUpdating = False
Mypath = "C:\Users\Kirank\Documents\Stock Feed Analysis\"
fileName = Dir(Mypath & "*.xl?") 'Dir functions support use of wildcards character * and ? used to look for all types of excel files in the folder.
Do While fileName <> ""
Count = Count + 1
mps = Split(fileName, " ")
For i = LBound(mps) To UBound(mps)
mps_temp = mps(UBound(mps) - i)
If mps_temp Like "####.##.##.xlsx" Then
mydate = DateSerial(Mid(mps_temp, 1, 4), Mid(mps_temp, 6, 2),Mid(mps_temp, 9,2))
IntroDate = mydate - 181
Cells(Count, 1).Value = IntroDate 'Saving Introdate in excel, feel free to change the destination.
Exit For
End If
Next i
fileName = Dir()
Loop
用法
function findById(tree, nodeId) {
for (let node of tree) {
if (node.id === nodeId) return node
if (node.children) {
let desiredNode = findById(node.children, nodeId)
if (desiredNode) return desiredNode
}
}
return false
}
为简化图片?,请想象一下:
现在让我们尝试将其应用于递归算法algorithm:
第一次进行学习递归似乎并不容易?,但是这种技术可以让您以优雅的方式解决日常问题。
答案 3 :(得分:1)
我知道这是一个古老的问题,但是由于最近又有了一个答案,我将把另一个版本混为一谈。
我将树遍历和测试与要测试的实际谓词分开。我相信这可以使代码更简洁。
基于reduce
的解决方案如下所示:
const nestedFind = (pred) => (xs) =>
xs .reduce (
(res, x) => res ? res : pred(x) ? x : nestedFind (pred) (x.children || []),
undefined
)
const findById = (testId) =>
nestedFind (({id}) => id == testId)
const data = [{id: 1}, {id: 2}, {id: 3}, {id: 4, children: [{id: 6}, {id: 7, children: [{id: 8}, {id: 9}]}]}, {id: 5}]
console .log (findById (8) (data))
console .log (findById (4) (data))
console .log (findById (42) (data))
.as-console-wrapper {min-height: 100% !important; top: 0}
有几种方法可以用主列表上的迭代替换reduce
。这样的事情会做同样的事情:
const nestedFind = (pred) => ([x = undefined, ...xs]) =>
x == undefined
? undefined
: pred (x)
? x
: nestedFind (pred) (x.children || []) || nestedFind (pred) (xs)
我们可以轻松完成尾递归操作。
尽管我们可以将这两个函数合而为一,并获得更短的代码,但我认为nestedFind
提供的灵活性将使其他类似问题更加容易。但是,如果您感兴趣,第一个可能看起来像这样:
const findById = (id) => (xs) =>
xs .reduce (
(res, x) => res ? res : x.id === id ? x : findById (id) (x.children || []),
undefined
)
答案 4 :(得分:0)
基于Purkhalo Alex解决方案,
我对他的功能进行了修改,使其能够基于给定的动态属性来递归地找到ID,然后返回您要查找的值还是之后递归地到达对象或属性的索引数组。 / p>
这就像find
和findIndex
一样,通过对象数组以及给定属性中的嵌套对象数组实现。
findByIdRecursive(tree, nodeId, prop = '', byIndex = false, arr = []) {
for (let [index, node] of tree.entries()) {
if (node.id === nodeId) return byIndex ? [...arr, index] : node;
if (prop.length && node[prop].length) {
let found = this.findByIdRecursive(node[prop], nodeId, prop, byIndex, [
...arr,
index
]);
if (found) return found;
}
}
return false;
}
现在您可以控制搜索的属性和类型并获得正确的结果。
答案 5 :(得分:0)
这可以通过reduce来解决。
const foundItem = data.reduce(findById(8), null)
function findById (id) {
const searchFunc = (found, item) => {
const children = item.children || []
return found || (item.id === id ? item : children.reduce(searchFunc, null))
}
return searchFunc
}
答案 6 :(得分:0)
您可以将Array.prototype.find()与Array.prototype.flatMap()结合使用
const findById = (a, id, p = "children", u) =>
a.length ? a.find(o => o.id === id) || findById(a.flatMap(o => o[p] || []), id) : u;
const tree = [{id:1}, {id:2}, {id:3}, {id:4, children:[{id: 6}, {id:7, children:[{id:8}, {id:9}]}]}, {id:5}];
console.log(findById(tree, 9)); // {id:9}
console.log(findById(tree, 10)); // undefined
答案 7 :(得分:0)
如果有人想使用 Array.prototype.find
,这是我选择的选项:
findById( my_big_array, id ) {
var result;
function recursiveFind( haystack_array, needle_id ) {
return haystack_array.find( element => {
if ( !Array.isArray( element ) ) {
if( element.id === needle_id ) {
result = element;
return true;
}
} else {
return recursiveFind( element, needle_id );
}
} );
}
recursiveFind( my_big_array, id );
return result;
}
您需要 result 变量,因为没有它,该函数将返回包含结果的数组中的顶级元素,而不是对包含匹配 id 的深层嵌套对象的引用,这意味着您需要过滤更进一步。
查看其他答案后,我的方法似乎与 Nina Scholz's 非常相似,但使用的是 find()
而不是 some()
。
答案 8 :(得分:0)
在我看来,如果你想通过 id 递归搜索,最好使用这样的算法:
function findById(data, id, prop = 'children', defaultValue = null) {
for (const item of data) {
if (item.id === id) {
return item;
}
if (Array.isArray(item[prop]) && item[prop].length) {
const element = this.findById(item[prop], id, prop, defaultValue);
if (element) {
return element;
}
}
}
return defaultValue;
}
findById(data, 2);
但我强烈建议使用更灵活的函数,它可以通过任何键值对/对进行搜索:
function findRecursive(data, keyvalues, prop = 'children', defaultValue = null, _keys = null) {
const keys = _keys || Object.keys(keyvalues);
for (const item of data) {
if (keys.every(key => item[key] === keyvalues[key])) {
return item;
}
if (Array.isArray(item[prop]) && item[prop].length) {
const element = this.findRecursive(item[prop], keyvalues, prop, defaultValue, keys);
if (element) {
return element;
}
}
}
return defaultValue;
}
findRecursive(data, {id: 2});
答案 9 :(得分:0)
这是一个不是最短的解决方案,而是将问题分解为递归迭代和在可迭代对象(不一定是数组)中查找项目。
您可以定义两个通用函数:
deepIterator
:以预序方式遍历森林的生成器iFind
:一个查找器,如 Array#find
,但适用于可迭代对象function * deepIterator(iterable, children="children") {
if (!iterable?.[Symbol.iterator]) return;
for (let item of iterable) {
yield item;
yield * deepIterator(item?.[children], children);
}
}
function iFind(iterator, callback, thisArg) {
for (let item of iterator) if (callback.call(thisArg, item)) return item;
}
// Demo
var data = [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4, children: [{ id: 6 }, { id: 7, children: [{ id: 8 }, { id: 9 }] }] }, { id: 5 }];
console.log(iFind(deepIterator(data), ({id}) => id === 8));
答案 10 :(得分:-1)
Roko C. Buljan 的解决方案,但更具可读性:
function findById(data, id, prop = 'children', defaultValue = null) {
if (!data.length) {
return defaultValue;
}
return (
data.find(el => el.id === id) ||
findById(
data.flatMap(el => el[prop] || []),
id
)
);
}