2

I have an array of objects.

{
  c1 : ["a1", "c2"],
  c2 : ["b1"],
  c3: ["d1"], 
  b1: ["e"]
  d1: ["k"]
}

I need the object to be arranged in a hierarchy. like this,

{ 
   c1: [{a1: null}, {
           c2: [{ 
              b1: "e"
           }]
       }],
   c3: [{ d1: "k" }]
}

Note that we can omit the array in last (deepest) key: value pair. This is what I have tried until now.

for (v in hash){ 
   hash[v].forEach(function(ar){
    if(hash[ar]){
        if (new_hash[v] == undefined){
            new_hash[v] = []
        }
        new_hash[v].push({[ar] : hash[ar]})
    }
   })
}

I think this problem requires dynamic programming (recursion with saving the state) in which I am not good. Please help.

1
  • 1
    I think you have an object whose properties are arrays, not the other way around. ;-) Where does new_hash come from? Commented May 6, 2019 at 6:36

3 Answers 3

1

You could take another hash table and store there the relation between all node and take out of this for the result only node which have no parents.

To overcom the problem of nodes without children, I added an empty array, because the original wanted structure has either null or no children at all, like this node

{ b1: "e" }

where as with a null marker it should be

{ b1: [{ e: null }] }

This solution features an empty array, which can be replaced by any other value.

{ b1: [{ e: [] }] }

var hash = { c1: ["a1", "c2"], c2: ["b1"], c3: ["d1"], b1: ["e"], d1: ["k"] },
    keys = Object.keys(hash),
    parents = new Set(keys),
    temp = {},
    tree ;

keys.forEach(k => hash[k].forEach(t => {
    parents.delete(t);
    temp[k] = temp[k] || [];
    temp[t] = temp[t] || [];
    if (!temp[k].some(o => t in o)) temp[k].push({ [t]: temp[t] });
}));

tree = Object.assign({}, ...Array.from(parents, k => ({ [k]: temp[k] })));

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

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

Comments

0

You can walk that parent-children list without recursion to get the tree.

I left out the code to actually transform the nodes into the format you're describing because it's pretty early in the morning for me, but the transformation should be pretty straightforward.

const data = {
  c1: ["a1", "c2"],
  c2: ["b1"],
  c3: ["d1"],
  b1: ["e"],
  d1: ["k"],
};

// Generate a hash of nodes, mapping them to their children and parents.
const nodes = {};
Object.entries(data).forEach(([parentId, childIds]) => {
  const parent = (nodes[parentId] = nodes[parentId] || {
    id: parentId,
    children: [],
  });
  childIds.forEach(childId => {
    const child = (nodes[childId] = nodes[childId] || {
      id: childId,
      children: [],
    });
    parent.children.push(child);
    child.parent = parent;
  });
});


// Filter in only the nodes with no parents
const rootNodes = {};
Object.values(nodes).forEach(node => {
  // TODO: transform the {id, children, parent} nodes to whichever format you require
  if (!node.parent) rootNodes[node.id] = node;
});

rootNodes will look like

{
  c1: {
    id: 'c1',
    children: [
      { id: 'a1', children: [], parent: ... },
      {
        id: 'c2',
        children: [
          {
            id: 'b1',
            children: [ { id: 'e', children: [], parent: ... } ],
            parent: ...
          }
        ],
        parent: ...
      }
    ]
  },
  c3: {
    id: 'c3',
    children: [
      {
        id: 'd1',
        children: [ { id: 'k', children: [], parent: ... } ],
        parent: ...
      }
    ]
  }
}

Comments

0

You could create one function with reduce method to loop Object.keys and build new object structure and one more function to check if current key is already in object and return it.

const data = {
  c1: ["a1", "c2"],
  c2: ["b1"],
  c3: ["d1"],
  b1: ["e"],
  d1: ["k"]
}

function find(obj, key) {
  let result = null
  for (let i in obj) {
    if (obj[i] === key || i === key) {
      result = obj
    }

    if (!result && typeof obj[i] == 'object') {
      result = find(obj[i], key)
    }
  }
  return result
}

function nest(data) {
  return Object.keys(data).reduce((r, e) => {
    const match = find(r, e);
    if (match) {
      if (!match[e]) match[e] = []
      match[e].push({
        [data[e]]: null
      })
    } else {
      data[e].forEach(el => {
        if (!r[e]) r[e] = [];
        r[e].push({
          [el]: null
        })
      })
    }
    return r;
  }, {})
}

const result = nest(data);
console.log(result)

Comments

Your Answer

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