【问题标题】:How to find a object in a nested array using recursion in JS如何在 JS 中使用递归在嵌套数组中查找对象
【发布时间】:2019-04-22 17:54:12
【问题描述】:

考虑以下深度嵌套的数组:

const array = [
    {
        id: 1,
        name: "bla",
        children: [
            {
                id: 23,
                name: "bla",
                children: [{ id: 88, name: "bla" }, { id: 99, name: "bla" }]
            },
            { id: 43, name: "bla" },
            {
                id: 45,
                name: "bla",
                children: [{ id: 43, name: "bla" }, { id: 46, name: "bla" }]
            }
        ]
    },
    {
        id: 12,
        name: "bla",
        children: [
            {
                id: 232,
                name: "bla",
                children: [{ id: 848, name: "bla" }, { id: 959, name: "bla" }]
            },
            { id: 433, name: "bla" },
            {
                id: 445,
                name: "bla",
                children: [
                    { id: 443, name: "bla" },
                    {
                        id: 456,
                        name: "bla",
                        children: [
                            {
                                id: 97,
                                name: "bla"
                            },
                            {
                                id: 56,
                                name: "bla"
                            }
                        ]
                    }
                ]
            }
        ]
    },
    {
        id: 15,
        name: "bla",
        children: [
            {
                id: 263,
                name: "bla",
                children: [{ id: 868, name: "bla" }, { id: 979, name: "bla" }]
            },
            { id: 483, name: "bla" },
            {
                id: 445,
                name: "bla",
                children: [{ id: 423, name: "bla" }, { id: 436, name: "bla" }]
            }
        ]
    }
];

我如何使用递归来通过键抓取某个可能深度嵌套的对象? 我试过这个,但这不适用于嵌套超过 2 层,然后它只会返回 undefined:

const findItemNested = (arr, itemId, nestingKey) => {
    for (const i of arr) {
        console.log(i.id);
        if (i.id === itemId) {
            return i;
        }
        if (i[nestingKey]) {
            findItemNested(i[nestingKey], itemId, nestingKey);
        }
    }
};

结果应该是:

const res = findItemNested(array, 959, "children"); >> { id: 959, name: "bla" }

这也许也可以使用.find 来实现,或者只是为了展平数组(通过 children 键),但使用递归对我来说似乎是最合乎逻辑的解决方案。有没有人可以解决这个问题?

提前致谢:)。

【问题讨论】:

  • 你需要return内部的findItemNested调用,否则它的返回值被丢弃。
  • 是的,这是我一直做错的一件事,谢谢!

标签: javascript recursion nested


【解决方案1】:

您需要遍历您的对象,然后需要使用递归解析每个对象。试试这里提到的答案:JavaScript recursive search in JSON object

代码:

`function findNode(id, currentNode) { 变量我, 当前孩子, 结果;

if (id == currentNode.id) {
    return currentNode;
} else {

    // Use a for loop instead of forEach to avoid nested functions
    // Otherwise "return" will not work properly
    for (i = 0; i < currentNode.children.length; i += 1) {
        currentChild = currentNode.children[i];

        // Search in the current child
        result = findNode(id, currentChild);

        // Return the result if the node has been found
        if (result !== false) {
            return result;
        }
    }

    // The node has not been found and we have no more options
    return false;
}

}`

【讨论】:

    【解决方案2】:

    你可以使用递归的reduce:

    const array=[{id:1,name:"bla",children:[{id:23,name:"bla",children:[{id:88,name:"bla"},{id:99,name:"bla"}]},{id:43,name:"bla"},{id:45,name:"bla",children:[{id:43,name:"bla"},{id:46,name:"bla"}]}]},{id:12,name:"bla",children:[{id:232,name:"bla",children:[{id:848,name:"bla"},{id:959,name:"bla"}]},{id:433,name:"bla"},{id:445,name:"bla",children:[{id:443,name:"bla"},{id:456,name:"bla",children:[{id:97,name:"bla"},{id:56,name:"bla"}]}]}]},{id:15,name:"bla",children:[{id:263,name:"bla",children:[{id:868,name:"bla"},{id:979,name:"bla"}]},{id:483,name:"bla"},{id:445,name:"bla",children:[{id:423,name:"bla"},{id:436,name:"bla"}]}]}];
    
    const findItemNested = (arr, itemId, nestingKey) => (
      arr.reduce((a, item) => {
        if (a) return a;
        if (item.id === itemId) return item;
        if (item[nestingKey]) return findItemNested(item[nestingKey], itemId, nestingKey)
      }, null)
    );
    const res = findItemNested(array, 959, "children");
    console.log(res);

    【讨论】:

    • 我不明白reduce函数中的“a”var何时受到影响?!
    • a 是累加器,是上次迭代返回的值。如果为真,则在过去的迭代中找到匹配项,并返回到下一次迭代。如果为 falsy,则尚未找到匹配项,因此 reduce 要么返回当前项(如果匹配):return item,要么搜索与 return findItemNested(... 的递归匹配项。
    • 非常感谢..我一直在寻找。
    【解决方案3】:

    这应该有效:

    function findByIdRecursive(array, id) {
      for (let index = 0; index < array.length; index++) {
        const element = array[index];
        if (element.id === id) {
          return element;
        } else {
          if (element.children) {
            const found = findByIdRecursive(element.children, id);
    
            if (found) {
              return found;
            }
          }
        }
      }
    }
    

    【讨论】:

    • 感谢您的回答!这是一种简单的方法。
    【解决方案4】:

    你可以这样做:

    const array=[{id:1,name:"bla",children:[{id:23,name:"bla",children:[{id:88,name:"bla"},{id:99,name:"bla"}]},{id:43,name:"bla"},{id:45,name:"bla",children:[{id:43,name:"bla"},{id:46,name:"bla"}]}]},{id:12,name:"bla",children:[{id:232,name:"bla",children:[{id:848,name:"bla"},{id:959,name:"bla"}]},{id:433,name:"bla"},{id:445,name:"bla",children:[{id:443,name:"bla"},{id:456,name:"bla",children:[{id:97,name:"bla"},{id:56,name:"bla"}]}]}]},{id:15,name:"bla",children:[{id:263,name:"bla",children:[{id:868,name:"bla"},{id:979,name:"bla"}]},{id:483,name:"bla"},{id:445,name:"bla",children:[{id:423,name:"bla"},{id:436,name:"bla"}]}]}];
    const findItemNested = (arr, itemId, nestingKey) => arr.reduce((a, c) => {
      return a.length
        ? a
        : c.id === itemId
          ? a.concat(c)
          : c[nestingKey]
            ? a.concat(findItemNested(c[nestingKey], itemId, nestingKey))
            : a
    }, []);
    const res = findItemNested(array, 959, "children");
    
    if (res.length) {
      console.log(res[0]);
    }

    【讨论】:

      【解决方案5】:

      您也可以使用Array.find 的递归,如下所示

      const array=[{id:1,name:"bla",children:[{id:23,name:"bla",children:[{id:88,name:"bla"},{id:99,name:"bla"}]},{id:43,name:"bla"},{id:45,name:"bla",children:[{id:43,name:"bla"},{id:46,name:"bla"}]}]},{id:12,name:"bla",children:[{id:232,name:"bla",children:[{id:848,name:"bla"},{id:959,name:"bla"}]},{id:433,name:"bla"},{id:445,name:"bla",children:[{id:443,name:"bla"},{id:456,name:"bla",children:[{id:97,name:"bla"},{id:56,name:"bla"}]}]}]},{id:15,name:"bla",children:[{id:263,name:"bla",children:[{id:868,name:"bla"},{id:979,name:"bla"}]},{id:483,name:"bla"},{id:445,name:"bla",children:[{id:423,name:"bla"},{id:436,name:"bla"}]}]}];
      
      
      function findById(arr, id, nestingKey) {
        
        // if empty array then return
        if(arr.length == 0) return
        
        // return element if found else collect all children(or other nestedKey) array and run this function
        return arr.find(d => d.id == id) 
            || findById(arr.flatMap(d => d[nestingKey] || []), id) 
            || 'Not found'
      }
      
      console.log(findById(array, 12, 'children'))
      
      console.log(findById(array, 483, 'children'))
      
      console.log(findById(array, 1200, 'children'))

      【讨论】:

        【解决方案6】:

        这将使用逐级递归查找,它会尝试在数组中查找项目,然后使用数组中每个项目的子项调用自身:

        新的浏览器会有 Array.prototype.flatten 但在这种情况下我已经单独添加了 flatten 函数。

        const array = [{"id":1,"name":"bla","children":[{"id":23,"name":"bla","children":[{"id":88,"name":"bla"},{"id":99,"name":"bla"}]},{"id":43,"name":"bla"},{"id":45,"name":"bla","children":[{"id":43,"name":"bla"},{"id":46,"name":"bla"}]}]},{"id":12,"name":"bla","children":[{"id":232,"name":"bla","children":[{"id":848,"name":"bla"},{"id":959,"name":"bla"}]},{"id":433,"name":"bla"},{"id":445,"name":"bla","children":[{"id":443,"name":"bla"},{"id":456,"name":"bla","children":[{"id":97,"name":"bla"},{"id":56,"name":"bla"}]}]}]},{"id":15,"name":"bla","children":[{"id":263,"name":"bla","children":[{"id":868,"name":"bla"},{"id":979,"name":"bla"}]},{"id":483,"name":"bla"},{"id":445,"name":"bla","children":[{"id":423,"name":"bla"},{"id":436,"name":"bla"}]}]}];
        
        
        const flatten = (arr) =>
          arr.reduce((result, item) => result.concat(item), []);
        const findBy = (findFunction, subItemsKey) => (array) =>
          //array is empty (can be when children of children of children does not exist)
          array.length === 0
            ? undefined //return undefined when array is empty
            : array.find(findFunction) || //return item if found
              findBy(findFunction, subItemsKey)(//call itself when item is not found
                flatten(
                  //take children from each item and flatten it
                  //([[child],[child,child]])=>[child,child,child]
                  array.map((item) => item[subItemsKey] || []),
                ),
              );
        const findChildrenById = (array) => (value) =>
          findBy((item) => item.id === value, 'children')(array);
        const findInArray = findChildrenById(array);
        
        console.log('found', findInArray(99));
        console.log('not found', findInArray({}));

        【讨论】:

          【解决方案7】:

          我们使用object-scan 进行大部分数据处理。它对各种事情都很棒,但确实需要一段时间才能理解。可以这样回答您的问题:

          // const objectScan = require('object-scan');
          
          const find = (data, id) => objectScan(['**(^children$).id'], {
            abort: true,
            rtn: 'parent',
            useArraySelector: false,
            filterFn: ({ value }) => value === id
          })(data);
          
          const array=[{id:1,name:"bla",children:[{id:23,name:"bla",children:[{id:88,name:"bla"},{id:99,name:"bla"}]},{id:43,name:"bla"},{id:45,name:"bla",children:[{id:43,name:"bla"},{id:46,name:"bla"}]}]},{id:12,name:"bla",children:[{id:232,name:"bla",children:[{id:848,name:"bla"},{id:959,name:"bla"}]},{id:433,name:"bla"},{id:445,name:"bla",children:[{id:443,name:"bla"},{id:456,name:"bla",children:[{id:97,name:"bla"},{id:56,name:"bla"}]}]}]},{id:15,name:"bla",children:[{id:263,name:"bla",children:[{id:868,name:"bla"},{id:979,name:"bla"}]},{id:483,name:"bla"},{id:445,name:"bla",children:[{id:423,name:"bla"},{id:436,name:"bla"}]}]}];
          
          console.log(find(array, 12));
          // => { id: 12, name: 'bla', children: [ { id: 232, name: 'bla', children: [ { id: 848, name: 'bla' }, { id: 959, name: 'bla' } ] }, { id: 433, name: 'bla' }, { id: 445, name: 'bla', children: [ { id: 443, name: 'bla' }, { id: 456, name: 'bla', children: [ { id: 97, name: 'bla' }, { id: 56, name: 'bla' } ] } ] } ] }
          console.log(find(array, 483));
          // => { id: 483, name: 'bla' }
          console.log(find(array, 959));
          // => { id: 959, name: 'bla' }
          console.log(find(array, 1200));
          // => undefined
          .as-console-wrapper {max-height: 100% !important; top: 0}
          &lt;script src="https://bundle.run/object-scan@13.7.1"&gt;&lt;/script&gt;

          免责声明:我是object-scan的作者

          【讨论】:

          • object-scan 太棒了!我以前不知道,谢谢!
          猜你喜欢
          • 2018-02-14
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-10-24
          • 2019-02-11
          • 1970-01-01
          • 1970-01-01
          • 2015-02-12
          相关资源
          最近更新 更多