简体   繁体   中英

Javascript Recursion Tree Building

I am having an issue building a tree from a flat array. I am building a category -> subcategory tree in which the parent has subcategories as an array.

Here is what the flat array would look like:

[
  {
    "id": 1
  },
  {
    "id": 5,
  },
  {
    "id": 2,
    "parent_id": 1
  },
  {
    "id": 3,
    "parent_id": 1
  },
  {
    "id": 42,
    "parent_id": 5
  },
  {
    "id": 67,
    "parent_id": 5
  }
]

And this is what I need the result to look:

[
  {
    "id":1,
    "subcategories":[
      {
        "id":2,
        "parent_id":1
      },
      {
        "id":3,
        "parent_id":1
      }
    ]
  },
  {
    "id":5,
    "subcategories":[
      {
        "id":42,
        "parent_id":5
      },
      {
        "id":67,
        "parent_id":5
      }
    ]
  }
]

I have tried to do this recursively by recursively searching for children and attaching it as an array and continuing to do so until I hit the bottom of the barrel but I am getting a cyclic structure. It appears that the parent_id in traverse is always the id of the parent... any ideas:

tree(passingInFlatObjectHere);

function topLevel (data) {
  let blob = [];
  data.forEach((each) => {
    if (!each.parent_id) {
      blob.push(each);
    }
  });
  return blob;
}

function tree (data) {
  let blob = topLevel(data).map(function (each) {
    each.subcategories = traverse(data, each.id);
    return each;
  });
  return blob;
}

function traverse (data, parent_id) {
  let blob = [];
  if (!parent_id) {
    return blob;
  }
  data.forEach((each) => {
    if (each.id === parent_id) {
      each.subcategories = traverse(data, each.id);
      blob.push(each);
    }
  });
  return blob;
}

I don't just want to help you fix your problem but would also like to help you take full advantage of ES6

First of all your topLevel function can be rewritten to this:

function topLevel(data) {
  return data.filter(node => !node.parent_id);
}

Neat isn't it? I also would recommend slightly changing tree for consistency but that's of course just stylistic.

function tree(data) {
  return topLevel(data).map(each => {
    each.subcategories = traverse(data, each.id);
    return each;
  });
}

No logic issues so far. traverse , however, contains one, when you check each.id === parent_id . Like this, the functions searches for the node whose id is parent_id . Obviously a mistake. You wanted each.parent_id === parent_id .

Your issue is solved now. Stop reading if I bother you. But you could also take advantage of filter here and remove that slightly superfluous early exit and rewrite your function to:

function traverse(data, parentId) {
  const children = data.filter(each => each.parent_id === parentId);
  children.forEach(child => {
    child.subcategories = traverse(data, child.id);
  });
  return children;
}

First, you need install lodash with below command with npm:

npm i lodash

Second, you must import _ from lodash

import _ from "lodash";

Finally, run this function:

export const recursive_lists = (data) => {
  const grouped = _.groupBy(data, (item) => item. parent_id);

  function childrenOf(parent_id) {
    return (grouped[parent_id] || []).map((item) => ({
      id: item.id,
      child: childrenOf(item.id),
    }));
  }

  return childrenOf(null);
};

or

First:

<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.20/lodash.min.js"></script>

Second:

function recursive_lists(data) {
  const grouped = _.groupBy(data, (item) => item.parent_id);

  function childrenOf(parent_id) {
    return (grouped[parent_id] || []).map((item) => ({
      id: item.id,
      child: childrenOf(item.id),
    }));
  }

  return childrenOf(null);
};

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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