KareimQ
KareimQ

Reputation: 15

Formatting JSON data for Tree Graph

I have a JSON array of the following format (this data is pulled from mongodb to be displayed as a tree graph on a react-based front-end):

[
  {
    "name": "7654321",
    "children": [
      {
        "_id": "LjYgocn9PsHhEFbM7",
        "accountId": "4343213"
      },
      {
        "_id": "sB2ipCstYnLnHrAuu",
        "accountId": "4343271"
      },
      {
        "_id": "JhugmhxS7A57Y34wM",
        "accountId": "4343276"
      }
    ]
  },
  {
    "name": "4343213",
    "children": [

    ]
  },
  {
    "name": "4343271",
    "children": [
      {
        "_id": "sie9mtttgdRw7Ktma",
        "accountId": "4343279"
      }
    ]
  },
  {
    "name": "4343279",
    "children": [
      {
        "_id": "sie23mtttgdRw7Ktma",
        "accountId": "8765345"
      }
    ]
  },
  {
    "name": "4343276",
    "children": [

    ]
  }
]

The goal is to re-format (rename and delete some keys) this data to be used in react-tree-graph. From the sample above, output should look like:

[
  {
    "name": "7654321",
    "children": [
      {
        "name": "4343213"
      },
      {
        "name": "4343271",
        "children": [
          {
            "name": "4343279",
            "children": [
                {
                  "name": "8765345"
                }
            ]
          }
        ]
      },
      {
        "name": "4343276"
      }
    ]
  }
]

Any help is appreciated!

Upvotes: 0

Views: 675

Answers (1)

trincot
trincot

Reputation: 350077

You could first create a Map that has as keys the name property values, and as corresponding values the (unfinished) result objects. They start off with just the name property.

Then you can iterate the children information in the input, to wire the children into the above mentioned result objects, which can be done efficiently using the name as key in the Map.

Whenever you wire a child object into a parent object, you know that child is not a top-level object in the final result. So starting with all nodes, you would trim that list (a Set) all those nodes that occur in a children array. This will leave you with only the top level nodes, which in its array form represents the desired output.

Implementation:

let data = [{"name": "7654321","children": [{"_id": "LjYgocn9PsHhEFbM7","accountId": "4343213"},{"_id": "sB2ipCstYnLnHrAuu","accountId": "4343271"},{"_id": "JhugmhxS7A57Y34wM","accountId": "4343276"}]},{"name": "4343213","children": []},{"name": "4343271","children": [{"_id": "sie9mtttgdRw7Ktma","accountId": "4343279"}]},{"name": "4343279","children": [{"_id": "sie23mtttgdRw7Ktma","accountId": "8765345"}]},{"name": "4343276","children": []}];

let map = new Map(data.map(({name, children}) => [name, { name }]));
let roots = new Set(map.values());
for (let {name, children} of data) {
    if (!children?.length) continue;
    map.get(name).children = children.map(({accountId}) => {
        let child = map.get(accountId) || { name: accountId };
        roots.delete(child);
        return child;
    });
}
let result = Array.from(roots);

console.log(result);

Upvotes: 1

Related Questions