对象数组上的JavaScript递归搜索

时间:2018-12-26 23:29:23

标签: javascript

我有一系列对象,这些对象深深地嵌套了孩子,有时甚至是孩子中的孩子。我试图递归地处理此问题,但是我被卡住了。

该函数的目标是返回与id匹配的单个数据对象。

我的数据如下:

data: [
    {
      id: 'RAKUFNUBNY00UBZ40950',
      name: 'Grade 1 Cover',
      activityId: 'RAKUFNUBNY00UBZ40950',
      nodeType: 'activity',
      suppressed: false,
      hidden: false
    },
    {
      children: [
        {
          id: 'SLWDYEQHTZAFA3ALH195',
          name: 'Build Background Video',
          activityId: 'SLWDYEQHTZAFA3ALH195',
          nodeType: 'activity',
          suppressed: false,
          hidden: false,
          assetReference: {
            referenceId: 'UWFHA5A1E0EGKCM0W899',
            assetType: 'image'
          }
        },
        {
          children: [
            {
              id: 'HQUCD2SSRKMYC2PJM636',
              name: 'Eat or Be Eaten Splash Card',
              activityId: 'HQUCD2SSRKMYC2PJM636',
              nodeType: 'activity',
              suppressed: false,
              hidden: true
            },
            {
              children: [
                {
                  id: 'ZDTWEZFL13L8516VY480',
                  name: 'Interactive Work Text: Eat or Be Eaten',
                  activityId: 'ZDTWEZFL13L8516VY480',
                  nodeType: 'activity',
                  suppressed: false,
                  hidden: true,
                  defaultLaunchMode: 'modal'
                }
              ],

我解决这个问题的尝试是这样的:

findNode(id, currentNode) {
    console.log('id', id);
    console.log('findNode', currentNode);
    var i, currentChild, result, counter;
    counter = 0;
    console.log('first conditional statement', currentNode);
    if (id && currentNode.id === id) {
      return currentNode[0];
    } else {
      counter++;
      // Use a for loop instead of forEach to avoid nested functions
      // Otherwise "return" will not work properly
      console.log('counter', counter);
      console.log('currentNode', currentNode[counter]);
      console.log('currentNode Children', currentNode.children);
      for (i = counter; i < currentNode.children.length; i += 1) {
        console.log(currentNode[i].children[i]);
        currentChild = currentNode[i].children[i];

        // Search in the current child
        result = this.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;
    }
  }

上面的代码失败了,因为我很难跟踪计数器以遍历所有内容。 enter image description here

我还添加了数据输出的示例图片,以为您提供一个更好的数据结构示例。任何帮助将不胜感激。

3 个答案:

答案 0 :(得分:8)

您不需要计数器即可找到具有匹配的id的单个节点。试试这个更简单的方法:

function findNode (id, array) {
  for (const node of array) {
    if (node.id === id) return node
    if (node.children) {
      const node = findNode(id, node.children)
      if (node) return node
    }
  }
}

如果没有匹配项,它将返回undefined

答案 1 :(得分:0)

为避免手动迭代的需要,您可以考虑使用reduce之类的数组方法-如果是真的,则返回累加器(即,已找到一个对象),或者返回要迭代的对象如果ID匹配,或递归地遍历对象的children以找到匹配项。

const data=[{id:'RAKUFNUBNY00UBZ40950',name:'Grade 1 Cover',activityId:'RAKUFNUBNY00UBZ40950',nodeType:'activity',suppressed:!1,hidden:!1},{children:[{id:'SLWDYEQHTZAFA3ALH195',name:'Build Background Video',activityId:'SLWDYEQHTZAFA3ALH195',nodeType:'activity',suppressed:!1,hidden:!1,assetReference:{referenceId:'UWFHA5A1E0EGKCM0W899',assetType:'image'}},{children:[{id:'HQUCD2SSRKMYC2PJM636',name:'Eat or Be Eaten Splash Card',activityId:'HQUCD2SSRKMYC2PJM636',nodeType:'activity',suppressed:!1,hidden:!0},{children:[{id:'ZDTWEZFL13L8516VY480',name:'Interactive Work Text: Eat or Be Eaten',activityId:'ZDTWEZFL13L8516VY480',nodeType:'activity',suppressed:!1,hidden:!0,defaultLaunchMode:'modal'}],}],}],}]

function findId(id, arr) {
  return arr.reduce((a, item) => {
    if (a) return a;
    if (item.id === id) return item;
    if (item.children) return findId(id, item.children);
  }, null);
}
console.log(findId('HQUCD2SSRKMYC2PJM636', data));

答案 2 :(得分:0)

如果id是唯一的,并且通过id查找对象是一项常见的任务,则您可能需要考虑创建一个查找对象以提高性能。创建查找对象是一项O(n)任务;然后,按id查找对象是O(1)。

const data = [ { id: 'RAKUFNUBNY00UBZ40950', name: 'Grade 1 Cover', activityId: 'RAKUFNUBNY00UBZ40950', nodeType: 'activity', suppressed: false, hidden: false }, { children: [ { id: 'SLWDYEQHTZAFA3ALH195', name: 'Build Background Video', activityId: 'SLWDYEQHTZAFA3ALH195', nodeType: 'activity', suppressed: false, hidden: false, assetReference: { referenceId: 'UWFHA5A1E0EGKCM0W899', assetType: 'image' } }, { children: [ { id: 'HQUCD2SSRKMYC2PJM636', name: 'Eat or Be Eaten Splash Card', activityId: 'HQUCD2SSRKMYC2PJM636', nodeType: 'activity', suppressed: false, hidden: true }, { children: [ { id: 'ZDTWEZFL13L8516VY480', name: 'Interactive Work Text: Eat or Be Eaten', activityId: 'ZDTWEZFL13L8516VY480', nodeType: 'activity', suppressed: false, hidden: true, defaultLaunchMode: 'modal' } ] } ] } ] } ];

const lookup = {};

const registerIds = a => {
  a.forEach(o => {
    if ('id' in o) {
      lookup[o.id] = o;
    } else if ('children' in o) {
      registerIds(o.children)
    }
  });
}

registerIds(data);

console.log(lookup)