2

Essentially I want to build a new structure given an array of objects. The object payload would look like the array of elements below.

const data = [
{
  path: [],
  elements: ['Parent A']
},
{
  path: ['Parent A'],
  elements: ['Child A1', 'Child A2', 'Parent B']
},
{
  path: ['Parent A', 'Parent B'],
  elements: ['Child B1', 'Parent C']
},
{
  path: ['Parent A', 'Parent B', 'Parent C'],
  elements: ['Child C1']
},
{
  path: ['Parent D'],
  elements: ['Child D1']
},
]

The above payload would then get translated into the custom object that would look like this.

let newObject =
[
 {
  elem: "",
  children: [
   {
    elem: "Parent A",
    children: [
     {
      elem: "Child A1",
      children: []
     },
     {
      elem: "Child A2",
      children: []
     },
     {
      elem: "Parent B",
      children: [
      {
       elem: "Child B1",
       children: []
      },
      {
       elem: "Parent C",
       children: [
       {
        elem: "Child C1",
        children: []
       }
       ]
      },
      ]
     },
    ]
   }
  ]
 },
 {
  elem: "Parent D",
  children: [
   {
    elem: "Child D1",
    children: []
   }
  ]
 }
]

I've considered making a map where I've given each element a visited property (to avoid recreating an element) and doing a recursive approach to populate a potential parent. However, I'm currently stuck and looking for feedback on what data structure/approach I should use.

My code for the mapper

const stringifyPath = (arr) => {
    return arr.join('>');
}

let elemMap = new Map();

data.forEach(item => {
    const itemPath = stringifyPath(item.path);
    if (!elemMap.get(itemPath)) {
        elemMap.set(itemPath, {
            elements: [],
            visited: false
        })
    }
    elemMap.get(itemPath).elements = [...elemMap.get(itemPath).elements, ...item.elements];
});

This yields

{
  '' => { elements: [ 'Parent A' ], visited: false },
  'Parent A' => { elements: [ 'Child A1', 'Child A2', 'Parent B' ],
    visited: false },
  'Parent A>Parent B' => { elements: [ 'Child B1', 'Parent C' ], visited: false },
  'Parent A>Parent B>Parent C' => { elements: [ 'Child C1' ], visited: false },
  'Parent D' => { elements: [ 'Child D1' ], visited: false } }

Any feedback is appreciated, thank you!

3
  • 1
    please add your code as well. Commented Feb 7, 2020 at 16:41
  • are the elements unique? Commented Feb 7, 2020 at 16:43
  • They don't have to be, they can be duplicates. Commented Feb 7, 2020 at 16:50

2 Answers 2

1

You could have a straight forward approach by reducing the path with finding the object for each nested level and push new elements to the found object's children.

For me, it makes no sense to store all pathes, because you need them only once for building the structure.

var data = [{ path: [], elements: ['Parent A'] }, { path: ['Parent A'], elements: ['Child A1', 'Child A2', 'Parent B'] }, { path: ['Parent A', 'Parent B'], elements: ['Child B1', 'Parent C'] }, { path: ['Parent A', 'Parent B', 'Parent C'], elements: ['Child C1'] }, { path: ['Parent D'], elements: ['Child D1'] }],
    result = data.reduce((r, { path, elements }) => {
        path
            .reduce((array, elem) => {
                var temp = array.find(q => q.elem === elem);
                if (!temp) array.push(temp = { elem, children: [] });
                return temp.children;
            }, r)
            .push(...elements.map(elem => ({ elem, children: [] })));
        return r;
    }, []);

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

Sign up to request clarification or add additional context in comments.

Comments

1

This might be useful.

const data = [{ path: ['Parent A'], elements: ['Child A1', 'Child A2', 'Parent B'] }, 
              { path: ['Parent A', 'Parent B'], elements: ['Child B1', 'Parent C'] }, 
              { path: ['Parent A', 'Parent B', 'Parent C'], elements: ['Child C1'] }, 
              { path: ['Parent D'], elements: ['Child D1'] }, 
              { path: ['Parent A', 'Parent B', 'Parent F'], elements: ['Child F1', 'Child F2'] } ]
    
const toTree = (d) => d.reduce((tree, c) => (createPath(tree, c), tree), [])

const createPath = (t, { path: [ name, ...rest ], elements }, node) => 
    (node = findInPeers(t, name) || addNode(t, name, rest.length ? [] : elements), 
     (rest.length && createPath(node.children, { path: rest, elements })), t)

const findInPeers = (t, name) => t.find(({ elem }) => elem === name)

const addNode = (t, name, elements, node) => 
    (t.push(node = { elem: name, children: asNodes(elements) }), node)

const asNodes = (elements) => elements.map((e)=>({ elem: e, children: [] }))

console.log(toTree(data))

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.