Home > OS >  Objects tree from arrays
Objects tree from arrays

Time:12-13

Sample input:

const input = [
  ['Item 1'],
  ['Item 1', 'Item 2'],
  ['Item 1', 'Item 3'],
  ['Item 1', 'Item 4', 'Item 5'],
  ['Item 8', 'Item 9'],
  ['Item 10', 'Item 11', 'Item 12', 'Item 13']
]

I need to get a tree like this:

const output = [
  { 
    name: 'Item 1', 
    path: 'Item 1',
    children: [
      {
        name: 'Item 2',
        path: 'Item 1, Item 2'
      },
      {
        name: 'Item 3',
        path: 'Item 1, Item 3'
      },
      {
        name: 'Item 4',
        path: 'Item 1, Item 4',
        children: [
          {
            name: 'Item 5',
            path: 'Item 1, Item 4, Item 5'
          }
        ]
      },
    ] 
  },
  {
    name: 'Item 8', 
    path: 'Item 8',
    children: [
      {
        name: 'Item 9',
        path: 'Item 8, Item 9'
      }
    ]
  },
  {
    name: 'Item 10',
    path: 'Item 10',
    children: [
      {
        name: 'Item 11',
        path: 'Item 10, Item 11',
        children: [
          {
            name: 'Item 12',
            path: 'Item 10, Item 11, Item 12',
            children: [
              {
                name: 'Item 13',
                path: 'Item 10, Item 11, Item 12, Item 13'
              }
            ]
          }
        ]
      },
    ]
  }
]

I tried:

let data = Object.fromEntries(arrays.map(el => [el[0], { name: el[0], path: `${el[0]}` }]))

for (let arr of arrays) {
  let datNow = data[arr[0]]
  for (var i = 1; i < arr.length; i  ) {
    if (!datNow.hasOwnProperty('children'))
      datNow.children = [
        {
          name: arr[i],
          path: `${arr[i]},${datNow.name}`
        }
      ]
    else if (!datNow.children.some(el => el.name == arr[i])) {
      datNow.children.push({
        name: arr[i],
        path: `${arr[i]},${datNow.name}`
      })
    }

    datNow = datNow.children.find(el => el.name == arr[i])
  }
}

console.log(Object.values(data))

But this option does not save the path beyond two parents. The other variants either throwing error, or generated completely wrong tree

As I understand it is possible to do this with a recursive function call, but I can not either generate the tree correctly, or the attribute "path". What could be the solution to this issue?

CodePudding user response:

Based on this answer: https://stackoverflow.com/a/57344801/3807365.

const input = [
  ['Item 1'],
  ['Item 1', 'Item 2'],
  ['Item 1', 'Item 3'],
  ['Item 1', 'Item 4', 'Item 5'],
  ['Item 8', 'Item 9'],
  ['Item 10', 'Item 11', 'Item 12', 'Item 13']
]

let agg = {
  _temp: []
};

input.forEach(path => {
  path.reduce((agg, part, level, parts) => {
    if (!agg[part]) {
      agg[part] = {
        _temp: []
      };
      agg._temp.push({
        name: part,
        path: parts.slice(0, level   1).join(", "),
        children: agg[part]._temp
      })
    }
    return agg[part];
  }, agg)
})

const output = agg._temp;
console.log(output)
.as-console-wrapper {
  max-height: 100% !important;
}

Explanation:

For each of the input paths, given a path we are building a tree upon helper object. We do this step by step as we go on each part of the path. But as we do, we are creating arrays in the result object (which is under property _temp) - arrays of items of the same level in the format {name, path, children}. Finally, we ignore this entire helper tree and only return the first level array - which is the result (under property _temp).

TODO: rename _temp to a name not possible by any path. for example ?temp

CodePudding user response:

You could iterate the data and the path to the leaves.

const
    input = [['Item 1'], ['Item 1', 'Item 2'], ['Item 1', 'Item 3'], ['Item 1', 'Item 4', 'Item 5'], ['Item 8', 'Item 9'], ['Item 10', 'Item 11', 'Item 12', 'Item 13']],
    tree = input
        .reduce((r, path) => {
            path.reduce((level, name, i) => {
                let object = (level.children ??= []).find(q => q.name === name);
                if (!object) {
                    const path = path.slice(0, i   1).join(', ');
                    level.children.push(object = { name, path });
                }
                return object;        
            }, r);
            return r;
        }, { children: [] })
        .children;

console.log(tree);
.as-console-wrapper { max-height: 100% !important; top: 0; }

  • Related