简体   繁体   English

以递归方式在对象的深层嵌套数组中查找值,javascript

[英]Finding value in deep nested array of object, recursively, javascript

I have an array of nested objects, having parent-child relationship:我有一组嵌套对象,具有父子关系:

  [  
   {  
      "id":"5b9ce8d51dbb85944baddfa5",
      "name":"EARBANG",
      "parent_id":0,
      "status":"Inactive",
      "children":[  
         {  
            "id":"5b9ce8d5d978f75e4b1584ba",
            "name":"DIGINETIC",
            "parent_id":"5b9ce8d51dbb85944baddfa5",
            "status":"Active",
            "children":[  
               {  
                  "id":"5b9ce8d5cb79d63c8b38018c",
                  "name":"PREMIANT",
                  "parent_id":"5b9ce8d5d978f75e4b1584ba",
                  "status":"Active",
               }
            ]
         }
      ]
   },
   {  
      "id":"5b9ce8d51650fac75fa359c8",
      "name":"GEEKOLOGY",
      "parent_id":0,
      "status":"Active",
   },
   {  
      "id":"5b9ce8d59f52e801a2e40a97",
      "name":"TOYLETRY",
      "parent_id":0,
      "status":"Inactive",
   },
   {  
      "id":"5b9ce8d5d136fcfed2f3e0dd",
      "name":"PAPRIKUT",
      "parent_id":0,
      "status":"Inactive",
   },
   {  
      "id":"5b9ce8d53afb7a61e188c48e",
      "name":"EYERIS",
      "parent_id":0,
      "status":"Inactive",
   }
]

here I want that :在这里我想要:

1- Find an object having id eg 5b9ce8d51dbb85944baddfa5 1- 找到一个具有 id 的对象,例如5b9ce8d51dbb85944baddfa5

2- iterate in that objects children array (if non empty) recursively and get id of all its childrends and grand-childrens and great-grand-childrens in an array. 2- 递归迭代该对象的子数组(如果非空),并在数组中获取其所有子、孙子和曾孙子的 id。

So my result will be like所以我的结果会像

{
    "id":"5b9ce8d51dbb85944baddfa5",
    childs: ["5b9ce8d5d978f75e4b1584ba", "5b9ce8d5cb79d63c8b38018c", ...]
}

I tried some solutions available on stack overflow, but could not get it to work.我尝试了一些堆栈溢出可用的解决方案,但无法使其正常工作。

I appreciate if anyone can help me, my DS not that strong.如果有人可以帮助我,我很感激,我的 DS 没有那么强。

Thanks谢谢

Here is a search recursive function:这是一个搜索递归函数:

function searchRecursive(data, id) {
  let found = data.find(d => d.id === id);
  if (!found) {
    let i = 0;
    while(!found && i < data.length) {
      if (data[i].children && data[i].children.length) {
        found = searchRecursive(data[i].children, id);
      }
      i++;
    }
  }
  return found;
}

Recursively search for the id.递归搜索id。

 const data = [ { "id":"5b9ce8d51dbb85944baddfa5", "name":"EARBANG", "parent_id":0, "status":"Inactive", "children":[ { "id":"5b9ce8d5d978f75e4b1584ba", "name":"DIGINETIC", "parent_id":"5b9ce8d51dbb85944baddfa5", "status":"Active", "children":[ { "id":"5b9ce8d5cb79d63c8b38018c", "name":"PREMIANT", "parent_id":"5b9ce8d5d978f75e4b1584ba", "status":"Active", } ] } ] }, { "id":"5b9ce8d51650fac75fa359c8", "name":"GEEKOLOGY", "parent_id":0, "status":"Active", }, { "id":"5b9ce8d59f52e801a2e40a97", "name":"TOYLETRY", "parent_id":0, "status":"Inactive", }, { "id":"5b9ce8d5d136fcfed2f3e0dd", "name":"PAPRIKUT", "parent_id":0, "status":"Inactive", }, { "id":"5b9ce8d53afb7a61e188c48e", "name":"EYERIS", "parent_id":0, "status":"Inactive", } ]; const search = (data, id) => data.find(d => d.id === id) || search(d.children); console.log(search(data, '5b9ce8d51dbb85944baddfa5'));

Take a look at the code sandbox that I have created which recursively searches for id.看看我创建的代码沙箱,它递归地搜索 id。 Once id is found it calls another recursion to generate child's array.一旦找到 id,它就会调用另一个递归来生成子数组。

console.log(findId(data, "5b9ce8d51dbb85944baddfa5"));
console.log(findId(data, "5b9ce8d5cb79d63c8b38018c"));

Following is the output for above two.以下是以上两个的输出。

在此处输入图片说明

https://codesandbox.io/s/m4vowz8qp8 https://codesandbox.io/s/m4vowz8qp8

You can find the object with a fairly standard recursive approach.您可以使用相当标准的递归方法找到对象。 The edge condition is that the object passed to the function is an array.边缘条件是传递给函数的对象是数组。 This will do a depth first search for the ID.这将对 ID 进行深度优先搜索。

Once you find the object you need to get the descendant children.找到对象后,您需要获取后代子项。 I would make this a separate function for simplicity:为简单起见,我会将其作为一个单独的函数:

 const data = [ { "id":"5b9ce8d51dbb85944baddfa5","name":"EARBANG","parent_id":0,"status":"Inactive","children":[ { "id":"5b9ce8d5d978f75e4b1584ba","name":"DIGINETIC","parent_id":"5b9ce8d51dbb85944baddfa5","status":"Active","children":[ { "id":"5b9ce8d5cb79d63c8b38018c","name":"PREMIANT","parent_id":"5b9ce8d5d978f75e4b1584ba","status":"Active",}]}]},{ "id":"5b9ce8d51650fac75fa359c8","name":"GEEKOLOGY","parent_id":0,"status":"Active",},{ "id":"5b9ce8d59f52e801a2e40a97","name":"TOYLETRY","parent_id":0,"status":"Inactive",},{ "id":"5b9ce8d5d136fcfed2f3e0dd","name":"PAPRIKUT","parent_id":0,"status":"Inactive",},{ "id":"5b9ce8d53afb7a61e188c48e","name":"EYERIS","parent_id":0,"status":"Inactive",}]; // create an array of child ids function getChildren(obj) { return (!Array.isArray(obj)) ? [] : obj.reduce((arr, curr) => arr.concat(curr.id, ...getChildren(curr.children)), []) } // find a particular id function search(arr, key){ if (Array.isArray(arr)) { for (obj of arr){ return (obj.id === key) ? {id: obj.id, childs: getChildren(obj.children)} // call getChildren once you've found the object : search(obj.children, key) } } } console.log(search(data, '5b9ce8d51dbb85944baddfa5')); // find deeper nesting: console.log(search(data, '5b9ce8d5d978f75e4b1584ba'));

It will return undefined if the ID is not found.如果未找到 ID,它将返回 undefined。

 let input = [ { id: '5b9ce8d51dbb85944baddfa5', name: 'EARBANG', parent_id: 0, status: 'Inactive', children: [ { id: '5b9ce8d5d978f75e4b1584ba', name: 'DIGINETIC', parent_id: '5b9ce8d51dbb85944baddfa5', status: 'Active', children: [ { id: '5b9ce8d5cb79d63c8b38018c', name: 'PREMIANT', parent_id: '5b9ce8d5d978f75e4b1584ba', status: 'Active' } ] } ] }, { id: '5b9ce8d51650fac75fa359c8', name: 'GEEKOLOGY', parent_id: 0, status: 'Active' }, { id: '5b9ce8d59f52e801a2e40a97', name: 'TOYLETRY', parent_id: 0, status: 'Inactive' }, { id: '5b9ce8d5d136fcfed2f3e0dd', name: 'PAPRIKUT', parent_id: 0, status: 'Inactive' }, { id: '5b9ce8d53afb7a61e188c48e', name: 'EYERIS', parent_id: 0, status: 'Inactive' } ]; function getNestedChildrenId(fileteredObject, children) { return fileteredObject.map(item => { children.push(item.id); if (item.children && item.children.length) { getNestedChildrenId(item.children, children); } }); } function getParentAndChildrenId(parentId, data) { let result = { id: parentId, children: [] }; let fileteredParent = data.find(({ id }) => id === parentId); if (fileteredParent.children) getNestedChildrenId(fileteredParent.children, result.children); return result; } console.log(getParentAndChildrenId('5b9ce8d51dbb85944baddfa5', input)); console.log(getParentAndChildrenId('5b9ce8d5d136fcfed2f3e0dd', input));

Here's a basic recursive way,这是一个基本的递归方式,

 var data = [{ "id": "5b9ce8d51dbb85944baddfa5", "name": "EARBANG", "parent_id": 0, "status": "Inactive", "children": [{ "id": "5b9ce8d5d978f75e4b1584ba", "name": "DIGINETIC", "parent_id": "5b9ce8d51dbb85944baddfa5", "status": "Active", "children": [{ "id": "5b9ce8d5cb79d63c8b38018c", "name": "PREMIANT", "parent_id": "5b9ce8d5d978f75e4b1584ba", "status": "Active", }] }] }, { "id": "5b9ce8d51650fac75fa359c8", "name": "GEEKOLOGY", "parent_id": 0, "status": "Active", }, { "id": "5b9ce8d59f52e801a2e40a97", "name": "TOYLETRY", "parent_id": 0, "status": "Inactive", }, { "id": "5b9ce8d5d136fcfed2f3e0dd", "name": "PAPRIKUT", "parent_id": 0, "status": "Inactive", }, { "id": "5b9ce8d53afb7a61e188c48e", "name": "EYERIS", "parent_id": 0, "status": "Inactive", }]; function findChildren(obj, output, targetId, found) { var _found; obj.forEach((child) => { if (found || child.id === targetId) { if (found) output.push(child.id); _found = true; } if (child.children && child.children.length) { findChildren(child.children, output, targetId, _found); } }); return output; } var res = findChildren(data, [], "5b9ce8d51dbb85944baddfa5"); console.log('res:', res);

Depth-First Descent深度优先下降

Here is a generic implementation that stops recursing once the predicate function returns true (hopefully we can find gold in our loot):这是一个通用实现,一旦谓词函数返回 true,它就会停止递归(希望我们能在我们的战利品中找到黄金):

var loot = {
        box_a: 'seaweed',
        box_b: {
                box_c: [
                        { box_d: 'tuna', box_e: ['gold', 'trash'] }
                ]
        }
};

function descend_obj(item, pred_fn) {
        if (item instanceof Array) {
                return item.some(it => {
                        return descend_obj(it, pred_fn);
                });
        } else if (typeof item === "object") {
                return Object.keys(item).some(k => {
                        return descend_obj(item[k], pred_fn);
                });
        } else {
                return pred_fn(item);
        }
}

let hasGold = descend_obj(loot, function isGold(item) {
        return (item === 'gold')
});

let msg = (hasGold)
        ? 'yaaaay!'
        : 'awwweee';
console.log(msg);//yaaaay!

You can use this approach:您可以使用这种方法:

  var array = [your array];

  function filter(array, id) {
   result = {
    id: id,
    children: []
   };

   array.map(val => {
    if (val.id == id) {
     findChildren(val, result);
    }
   });

   return result;
  }

  function findChildren(obj) {
   if (obj.children && Array.isArray(obj.children)) {
    obj.children.map(val => {
     if (val.id) {
      result.children.push(val.id);
     }
     if (val.children) {
      findChildren(val);
     }
    });
   }
  }

  var search = filter(array, '5b9ce8d51dbb85944baddfa5');

I think the code is clear and does not need explanation.我认为代码很清楚,不需要解释。

I tend not to use recursion because recursion is harder to reason about that loops because:我倾向于不使用递归,因为递归更难推理循环,因为:

  1. To use recursion you have to know exactly what a function returns beforehand.要使用递归,您必须事先确切知道函数返回什么。 Because you will call it before it is complete (in its own body).因为你会在它完成之前调用它(在它自己的主体中)。
  2. With recursion, you have to keep in mind multiple copies of the same function because that's how recursion works - the function calls itself before it ends.对于递归,您必须牢记同一函数的多个副本,因为这就是递归的工作原理 - 函数在结束之前调用自身。 It's hard not to get lost in a pile of exactly same objects.很难不迷失在一堆完全相同的物体中。 With loops is easier because one iteration ends before another starts.使用循环更容易,因为一次迭代在另一次开始之前结束。

As far as synchronous code is concerned, recursion can always be replaced with a loop.就同​​步代码而言,递归总是可以用循环代替。

Here's non recursive solution:这是非递归解决方案:

function findDescendants(data, key) {
  let ancestor;
  const ancestorStack = [data];      
  while (ancestorStack.length) { // 1- Find an object having id
    const current = ancestorStack.pop();
    for (const item of current) {
      if (item.id === key) {
        ancestor = item;
        break;
      }
      const { children } = current;
      if (children && children.length) {
        ancestorStack.push(children);
      }
    }
  }
  const descendantIds = [];      
  const childrenStack = [ancestor.children];
  while (childrenStack.length) { // 2- get id of all its descendants
    const current = childrenStack.pop();
    for (const item of current) {
      descendantIds.push(item.id);
      const { children } = item;
      if (children && children.length) {
        childrenStack.push(children);
      }
    }
  }
  return {
    id: ancestor.id,
    childs: descendantIds
  }
}

live demo: https://repl.it/@marzelin/findDescendants现场演示: https : //repl.it/@marzelin/findDescendants

Since this function performs two independent tasks, it would be good to outsource and generalize the tasks:由于此功能执行两个独立的任务,因此最好将任务外包和泛化:

  • function findDeep(predicate, deepProps, data)
  • function gatherDeep(prop, deepProps, data)

Then findDecendants could be simplified to:然后findDecendants可以简化为:

function findDescendants(data, key) {
  const ancestor = findDeep(o => o.id === key, ["children"], data);
  const descendantIds = gatherDeep("id", ["children"], ancestor.children);
  return {
    id: ancestor.id,
    childs: descendantIds
  }
}

I made myself a function for this which might be of use for you as well.我为此创建了一个函数,它也可能对您有用。

It helps a lot when you dont know what kind of object you are dealing with...当你不知道你正在处理什么样的对象时,它会很有帮助......

Example:例子:

 var values = [ { "id":"5b9ce8d51dbb85944baddfa5", "name":"EARBANG", "parent_id":0, "status":"Inactive", "children":[ { "id":"5b9ce8d5d978f75e4b1584ba", "name":"DIGINETIC", "parent_id":"5b9ce8d51dbb85944baddfa5", "status":"Active", "children":[ { "id":"5b9ce8d5cb79d63c8b38018c", "name":"PREMIANT", "parent_id":"5b9ce8d5d978f75e4b1584ba", "status":"Active", } ] } ] }, { "id":"5b9ce8d51650fac75fa359c8", "name":"GEEKOLOGY", "parent_id":0, "status":"Active", }, { "id":"5b9ce8d59f52e801a2e40a97", "name":"TOYLETRY", "parent_id":0, "status":"Inactive", }, { "id":"5b9ce8d5d136fcfed2f3e0dd", "name":"PAPRIKUT", "parent_id":0, "status":"Inactive", }, { "id":"5b9ce8d53afb7a61e188c48e", "name":"EYERIS", "parent_id":0, "status":"Inactive", } ] //--------------------------------------------------------- // Strip Down to String Function //---------------------------------------------------------- var depth = 0; var stripdown = function(object, find, eol) { depth = depth + 1; var str = ""; var s = new function() { return { // Unique ID generator id: new Date().getFullYear() + new Date().getMonth() + new Date().getDate() + new Date().getHours() + new Date().getMinutes() + new Date().getSeconds() + new Date().getMilliseconds(), // Spaces for depth space: ' ', // Index index: 0 } }; while(s.index <= Object.keys(object).length){ // Every Last Line Has An Undefined Object So Clear Them if (Object.keys(object)[s.index] != undefined) { // If More Than 1 In The Line Add ',' + eol if (s.index > 0) { str = str + ',' + eol; } // Add Spaces For Each Depth Level for(var i=0;i < depth;i++) { str = str + s.space; } // Add Keys str = str + Object.keys(object)[s.index] + ' : '; // Add Values if (typeof Object.values(object)[s.index] === 'object'){ str = str + '{' + eol; // Starting Tag // Add Inner Object Values str = str + stripdown(Object.values(object)[s.index], find, eol); // Add Closing Space for(var i=depth;i > 0;i--) { str = str + s.space; } str = str + '}'; // Closing Tag // Find Object if(str.indexOf(find)>-1){ // Lower The Depth depth = depth - 1; return str + eol; } } else { // No Sub Object str = str + Object.values(object)[s.index]; } } // Next line s.index = s.index + 1; } // Lower The Depth depth = depth - 1; // Return return str + eol; } //--------------------------------------------------------- console.log(stripdown(values, "PREMIANT", "\\n"));

Usage:用法:

stripdown( [object Object], SearchFor, "\n" );

Returns Object Tree as String;以字符串形式返回对象树;

0 : {
    id : 5b9ce8d51dbb85944baddfa5,
    name : EARBANG,
    parent_id : 0,
    status : Inactive,
    children : {
        0 : {
            id : 5b9ce8d5d978f75e4b1584ba,
            name : DIGINETIC,
            parent_id : 5b9ce8d51dbb85944baddfa5,
            status : Active,
            children : {
                0 : {
                    id : 5b9ce8d5cb79d63c8b38018c,
                    name : PREMIANT,
                    parent_id : 5b9ce8d5d978f75e4b1584ba,
                    status : Active
                }
            }
        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM