0

I have a list (an array of objects), in which they can have parents or not.

const categories = [
  { value: 1, text: 'Food', parent_id: null },
  { value: 2, text: 'Services', parent_id: null },
  { value: 3, text: 'Amazon Prime', parent_id: 2 },
  { value: 4, text: 'Netlix', parent_id: 2 },
  { value: 5, text: 'Beauty', parent_id: null },
  { value: 6, text: 'Education', parent_id: null },
  { value: 8, text: 'Academy', parent_id: 6 },
  { value: 9, text: 'Taxes', parent_id: null },
  { value: 10, text: 'Fees', parent_id: 9 },
  { value: 11, text: 'Recreation', parent_id: null },
  { value: 12, text: 'Pub', parent_id: 11 },
  { value: 13, text: 'House', parent_id: null },
  { value: 15, text: 'Illumination', parent_id: 13 },
  { value: 16, text: 'Internet', parent_id: 13 },
  { value: 18, text: 'Pets', parent_id: null },
  { value: 19, text: 'Award', parent_id: null },
  { value: 20, text: 'Gifts', parent_id: null },
  { value: 21, text: 'Salary', parent_id: null },
  { value: 22, text: 'Health', parent_id: null },
  { value: 23, text: 'Doctor', parent_id: 22 },
  { value: 24, text: 'Pharmacy', parent_id: 22 },
  { value: 25, text: 'Transport', parent_id: null },
  { value: 26, text: 'Trip', parent_id: null },
  { value: 27, text: 'NY', parent_id: 27 },
  { value: 28, text: 'Investment', parent_id: 9 },
  { value: 29, text: 'Bakery', parent_id: 1 },
  { value: 30, text: 'Restaurant', parent_id: 1 },
  { value: 31, text: 'Marketplace', parent_id: 1 },
  { value: 32, text: 'Rent', parent_id: 13 },
  { value: 33, text: 'Phone', parent_id: 13 },
  { value: 34, text: 'Reforms', parent_id: 13 },
  { value: 35, text: 'Telecine', parent_id: 2 },
  { value: 36, text: 'Appliances', parent_id: 13 },
  { value: 37, text: 'Disney', parent_id: 2 },  
  { value: 38, text: 'Water', parent_id: 13 },
  { value: 39, text: 'Kitchen', parent_id: 35 },
  { value: 40, text: 'Furniture', parent_id: 40 },
  { value: 41, text: 'Construction Materials', parent_id: 40 },
]

export default categories;

I need to transform this list into a tree, making it from a comparison: if it has the tag_parent_id, and if it is equal to the value of some other item, it is a child! The result would have to be like this:

const categories = [
  { value: 1, text: 'Food', parent_id: null, children: [
    { value: 29, text: 'Bakery', parent_id: 1 },
    { value: 30, text: 'Restaurant', parent_id: 1 },
    { value: 31, text: 'Marketplace', parent_id: 1 },
  ] },
  { value: 2, text: 'Services', parent_id: null, children: [
    { value: 3, text: 'Amazon Prime', parent_id: 2 },
    { value: 4, text: 'Netlix', parent_id: 2 },
    { value: 35, text: 'Telecine', parent_id: 2 },
    { value: 37, text: 'Dinsey', parent_id: 2 },  
  ] },
  { value: 5, text: 'Beauty', parent_id: null },
  { value: 6, text: 'Education', parent_id: null, children: [
    { value: 8, text: 'Academy', parent_id: 6 },
  ] },
  { value: 9, text: 'Taxes', parent_id: null, children: [
    { value: 28, text: 'Investment', parent_id: 9 },
    { value: 10, text: 'Fees', parent_id: 9 },
  ] },
  { value: 11, text: 'Recreation', parent_id: null, children: [
    { value: 12, text: 'Pub', parent_id: 11 },
  ] },
  { value: 13, text: 'House', parent_id: null, children: [
    { value: 15, text: 'Illumination', parent_id: 13 },
    { value: 16, text: 'Internet', parent_id: 13 },
    { value: 32, text: 'Rent', parent_id: 13 },
    { value: 33, text: 'Phone', parent_id: 13 },
    { value: 34, text: 'Reforms', parent_id: 13, children: [
      { value: 39, text: 'Kitchen', parent_id: 36, children: [
        { value: 40, text: 'Furniture', parent_id: 39 },
        { value: 41, text: 'Construction Materials', parent_id: 39 },
      ] },
    ] },
    { value: 36, text: 'Appliances', parent_id: 13 },
    { value: 38, text: 'Water', parent_id: 13 },
  ] },
  { value: 18, text: 'Pets', parent_id: null },
  { value: 19, text: 'Award', parent_id: null },
  { value: 20, text: 'Gifts', parent_id: null },
  { value: 21, text: 'Salary', parent_id: null },
  { value: 22, text: 'Health', parent_id: null, children: [
    { value: 23, text: 'Doctor', parent_id: 22 },
    { value: 24, text: 'Pharmacy', parent_id: 22 },
  ] },
  { value: 25, text: 'Transport', parent_id: null },
  { value: 26, text: 'Trip', parent_id: null, children: [
    { value: 27, text: 'NY', parent_id: 26},
  ] },
]

I created the following function:

cascateCategories(state) {
      var end = [];
      getNestedChildren(state.categories)
      return end

      function getNestedChildren(categories) {
        for(let i in categories) {
          
          categories.forEach(category => {
            if(category.value == categories[i].parent_id) {
              
              if(!category.children) {
                category['children'] = [];
              }
              
              category.children.push(categories[i]);
              end.push(category)             
            }
          });          
          console.log(end);
        }
        return end;
      }
    },

However, he adds the same item several times, not just 1. In addition, he disregards items that do not have children. At the end of the day, I have a problem with the code and I don't know which one.

I am working with VueJS, Vuex and JavaScript.

adamgy
  • 4,543
  • 3
  • 16
  • 31
Grazi
  • 25
  • 7
  • You are looping twice over the same array, first with `for` and nested with `forEach`. That is causing the duplicates. – trincot Apr 08 '21 at 19:07

2 Answers2

0

You could take an object for collecting and return all nodes from the parent node.

const
    categories = [{ value: 1, text: 'Food', parent_id: null }, { value: 2, text: 'Services', parent_id: null }, { value: 3, text: 'Amazon Prime', parent_id: 2 }, { value: 4, text: 'Netlix', parent_id: 2 }, { value: 5, text: 'Beauty', parent_id: null }, { value: 6, text: 'Education', parent_id: null }, { value: 8, text: 'Academy', parent_id: 6 }, { value: 9, text: 'Taxes', parent_id: null }, { value: 10, text: 'Fees', parent_id: 9 }, { value: 11, text: 'Recreation', parent_id: null }, { value: 12, text: 'Pub', parent_id: 11 }, { value: 13, text: 'House', parent_id: null }, { value: 15, text: 'Illumination', parent_id: 13 }, { value: 16, text: 'Internet', parent_id: 13 }, { value: 18, text: 'Pets', parent_id: null }, { value: 19, text: 'Award', parent_id: null }, { value: 20, text: 'Gifts', parent_id: null }, { value: 21, text: 'Salary', parent_id: null }, { value: 22, text: 'Health', parent_id: null }, { value: 23, text: 'Doctor', parent_id: 22 }, { value: 24, text: 'Pharmacy', parent_id: 22 }, { value: 25, text: 'Transport', parent_id: null }, { value: 26, text: 'Trip', parent_id: null }, { value: 27, text: 'NY', parent_id: 27 }, { value: 28, text: 'Investment', parent_id: 9 }, { value: 29, text: 'Bakery', parent_id: 1 }, { value: 30, text: 'Restaurant', parent_id: 1 }, { value: 31, text: 'Marketplace', parent_id: 1 }, { value: 32, text: 'Rent', parent_id: 13 }, { value: 33, text: 'Phone', parent_id: 13 }, { value: 34, text: 'Reforms', parent_id: 13 }, { value: 35, text: 'Telecine', parent_id: 2 }, { value: 36, text: 'Appliances', parent_id: 13 }, { value: 37, text: 'Disney', parent_id: 2 }, { value: 38, text: 'Water', parent_id: 13 }, { value: 39, text: 'Kitchen', parent_id: 35 }, { value: 40, text: 'Furniture', parent_id: 40 }, { value: 41, text: 'Construction Materials', parent_id: 40 }],
    tree = function (data, root) {
        var t = {};
        data.forEach(o => {
            Object.assign(t[o.value] ??= {}, o);
            t[o.parent_id] ??= {};
            t[o.parent_id].children ??= [];
            t[o.parent_id].children.push(t[o.value]);
        });
        return t[root].children
    }(categories, null);

console.log(tree);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
0

Only add categories without parent in the result, add the others in the children of their parent:

// ....
cascateCategories(state) {
  const res = [];
  for (let category of state.categories) {
    if (category.parent_id == null) {
      res.push(category);
    } else {
      const parent = _.find(state.categories, { value: parent_id })
      if (parent.children == null) parent.children = [];
      parent.children.push(category);
    }
  }
  return res;
}

_ is lodash. If you import find from 'lodash/find', then you can omit the _.

If item with value: v is always at index v-1, we can set:

const parent = state.categories[parent_id - 1]

instead of

const parent = _.find(state.categories, { value: parent_id })

This function will change the items in the input array. It uses the fact that in JavaScript, variables store reference for objects, not values.

Nathan Xabedi
  • 1,097
  • 1
  • 11
  • 18
  • I liked your solution! Thanks. Can you explain to me what is happening on this line: const parent = _.find(state.categories, { value: parent_id }) – Grazi Apr 09 '21 at 11:48
  • [`_.find(state.categories, { value: parent_id })`](https://lodash.com/docs/2.4.2#find) returns the first item in `state.categories` with `item.value === parent_id` – Nathan Xabedi Apr 09 '21 at 15:15