Javascript 如何在返回单个对象时递归使用Array.prototype.find()?

Javascript 如何在返回单个对象时递归使用Array.prototype.find()?,javascript,recursion,Javascript,Recursion,我试图解决的更大问题是,考虑到这些数据: var data = [ { id: 1 }, { id: 2 }, { id: 3 }, { id: 4, children: [ { id: 6 }, { id: 7, children: [ {id: 8 }, {id: 9 } ]} ]}, { id: 5 } ] 我想创建一个返回{id:id}的函数f

我试图解决的更大问题是,考虑到这些数据:

var data = [
    { id: 1 },
    { id: 2 },
    { id: 3 },
    { id: 4, children: [
        { id: 6 },
        { id: 7, children: [
            {id: 8 },
            {id: 9 }
        ]}
    ]},
    { id: 5 }
]
我想创建一个返回
{id:id}
的函数
findById(data,id)
。例如,
findById(data,8)
应该返回
{id:8}
,而
findById(data,4)
应该返回
{id:4,子项:[……]}

为了实现这一点,我递归地使用了
Array.prototype.find
,但是当
return
不断地将对象混合在一起时遇到了问题。我的实现返回特定对象的路径

例如,当我使用
findById(data,8)
时,它返回
{id:8}
的路径:

 { id: 4, children: [ { id: 6 }, { id: 7, children: [ { id: 8}, { id: 9] } ] }
相反,我希望它只是返回

{ id: 8 }

实现(Node.js v4.0.0)


我将只使用常规循环和递归式搜索:

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}
函数findById(数据,id){
对于(变量i=0;i对象{id:4,子对象:数组[2]}
//findById(数据,8)=>对象{id:8}

您遇到的问题是查找的冒泡。如果在嵌套结构中找到id,回调将尝试返回元素(被解释为true)作为查找的值

find
方法对数组中的每个元素执行一次回调函数,直到找到回调返回真值的元素为止。[]

我建议不要使用find,而是使用递归样式进行搜索,如果找到,则使用短路

var data=[{id:1},{id:2},{id:3},{id:4,子项:[{id:6},{id:7,子项:[{id:8},{id:9}]},{id:5}];
函数findById(数据,id){
功能iter(a){
如果(a.id==id){
结果=a;
返回true;
}
返回数组.isArray(a.children)和&a.children.some(iter);
}
var结果;
数据部分(iter);
返回结果
}

log(findById(data,8))

让我们考虑基于递归调用的实现:

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
}
用法

要根据解决方案简化图片

我对他的函数进行了修改,使其能够基于给定的动态属性递归地查找ID,并返回您想要查找的值或一个索引数组,以便随后递归地到达对象或属性

这类似于
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;
}

现在,您可以控制查找的属性和类型,并获得正确的结果。

这可以通过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
}

我知道这是一个老问题,但随着最近另一个答案的出现,我将在混合中加入另一个版本

我将把树遍历和测试从我们想要测试的实际谓词中分离出来。我相信这会使代码更加简洁

基于
reduce
的解决方案可能如下所示:

const nestedFind=(pred)=>(xs)=>
减少(
(res,x)=>res?res:pred(x)?x:nestedFind(pred)(x.children | |[]),
未定义
)
常量findById=(testId)=>
nestedFind(({id})=>id==testId)
const data=[{id:1},{id:2},{id:3},{id:4,子项:[{id:6},{id:7,子项:[{id:8},{id:9}]},{id:5}]
console.log(findById(8)(数据))
console.log(findById(4)(数据))
console.log(findById(42)(数据))
.as控制台包装{min height:100%!important;top:0}
您可以与

constfindbyid=(a,id,p=“children”,u)=>
a、 长度?a、 find(o=>o.id==id)| | findById(a.flatMap(o=>o[p]| |[]),id):u;
常量树=[{id:1},{id:2},{id:3},{id:4,子项:[{id:6},{id:7,子项:[{id:8},{id:9}]},{id:5}];
console.log(findById(tree,9));//{id:9}

console.log(findById(tree,10));//未定义的
如果要使用数组.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的深度嵌套对象的引用,这意味着您需要进一步过滤它

通过查看其他答案,我的方法似乎非常类似,但使用了
find()
而不是
some()

的解决方案,但更具可读性:

函数findById(数据、id、属性='children',defaultValue=null){
如果(!data.length){
返回默认值;
}
返回(
data.find(el=>el.id==id)||
芬德比德(
data.flatMap(el=>el[prop]| |[]),
身份证件
)
);
}

在我看来,如果您想按id递归搜索,最好使用如下算法:

函数findById(数据、id、属性='children',defaultValue=null){
用于(常量数据项){
如果(item.id==id){
退货项目;
}
if(Array.isArray(item[prop])&item[prop].length){
const元素=this.findById(项[prop],id,prop,defaultValue);
if(元素){
返回元素;
}
}
}
返回默认值;
}
findById(数据,2);
但我强烈建议使用更灵活的函数,它可以按任意键值对进行搜索:

函数findRecursive(数据、keyvalues、prop='children',defaultValue=null,_keys=null){
常量键=_键| |对象.键(键值);
用于(常量数据项){
if(key.every(key=>item[key]==keyvalues[key])){
退货项目;
}
if(Array.isArray(item[prop])&item[prop].length){
常数
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
}
    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;
    }