2

i having problems converting an array of hierarchy data into the right Object format.

I trying to convert this:

[
    {"PARENT_ID": 0,"CHILD_ID": 1,"NAME": "Quality","LEVEL_A": 0},
    {"PARENT_ID": 1,"CHILD_ID": 2,"NAME": "Machine","LEVEL_A": 1},
    {"PARENT_ID": 1,"CHILD_ID": 3,"NAME": "Method","LEVEL_A": 1},
    {"PARENT_ID": 1,"CHILD_ID": 4,"NAME": "Material","LEVEL_A": 1},
    {"PARENT_ID": 1,"CHILD_ID": 5,"NAME": "Man Power","LEVEL_A": 1},
    {"PARENT_ID": 1,"CHILD_ID": 6,"NAME": "Measurement","LEVEL_A": 1},
    {"PARENT_ID": 1,"CHILD_ID": 7,"NAME": "Milieu","LEVEL_A": 1},
    {"PARENT_ID": 1,"CHILD_ID": 10,"NAME": "Metal Lathe","LEVEL_A": 1},
    {"PARENT_ID": 2,"CHILD_ID": 3,"NAME": "dasd","LEVEL_A": 2},
    {"PARENT_ID": 2,"CHILD_ID": 8,"NAME": "Mill","LEVEL_A": 2},
    {"PARENT_ID": 2,"CHILD_ID": 9,"NAME": "Mixer","LEVEL_A": 2},
    {"PARENT_ID": 4,"CHILD_ID": 11,"NAME": "Masonite","LEVEL_A": 2},
    {"PARENT_ID": 4,"CHILD_ID": 12,"NAME": "Meat","LEVEL_A": 2},
    {"PARENT_ID": 4,"CHILD_ID": 13,"NAME": "Marscapone","LEVEL_A": 2},
    {"PARENT_ID": 5,"CHILD_ID": 20,"NAME": "Manager","LEVEL_A": 2},
    {"PARENT_ID": 5,"CHILD_ID": 21,"NAME": "Magician","LEVEL_A": 2},
    {"PARENT_ID": 5,"CHILD_ID": 22,"NAME": "Magister","LEVEL_A": 2},
    {"PARENT_ID": 5,"CHILD_ID": 24,"NAME": "Massage Artist","LEVEL_A": 2},
    {"PARENT_ID": 6,"CHILD_ID": 25,"NAME": "Malleability","LEVEL_A": 2},
    {"PARENT_ID": 7,"CHILD_ID": 26,"NAME": "Marine","LEVEL_A": 2},
    {"PARENT_ID": 12,"CHILD_ID": 19,"NAME": "Mutton","LEVEL_A": 3},
    {"PARENT_ID": 13,"CHILD_ID": 14,"NAME": "Malty","LEVEL_A": 3},
    {"PARENT_ID": 13,"CHILD_ID": 15,"NAME": "Minty","LEVEL_A": 3},
    {"PARENT_ID": 14,"CHILD_ID": 17,"NAME": "Minty","LEVEL_A": 4},
    {"PARENT_ID": 17,"CHILD_ID": 16,"NAME": "spearMint","LEVEL_A": 5},
    {"PARENT_ID": 17,"CHILD_ID": 18,"NAME": "pepperMint","LEVEL_A": 5},
    {"PARENT_ID": 22,"CHILD_ID": 23,"NAME": "Malpractice","LEVEL_A": 3}
]

to:

{
        "PARENT_ID": 0,
        "CHILD_ID": 1,
        "NAME": "Quality",
        "LEVEL_A": 0,
        "children": [
            {
                "PARENT_ID": 1,
                "CHILD_ID": 2,
                "NAME": "Machine",
                "LEVEL_A": 1,
                "children": [
                    {
                        "PARENT_ID": 2,
                        "CHILD_ID": 3,
                        "NAME": "Banana",
                        "LEVEL_A": 2,
                        "children": []
                    },
                    {
                        "PARENT_ID": 2,
                        "CHILD_ID": 8,
                        "NAME": "Mill",
                        "LEVEL_A": 2,
                        "children": []
                    },
                    {
                        "PARENT_ID": 2,
                        "CHILD_ID": 9,
                        "NAME": "Mixer",
                        "LEVEL_A": 2,
                        "children": []
                    }
                ]
            },
            {
                "PARENT_ID": 1,
                "CHILD_ID": 3,
                "NAME": "Method",
                "LEVEL_A": 1,
                "children": []
            },
            {
                "PARENT_ID": 1,
                "CHILD_ID": 4,
                "NAME": "Material",
                "LEVEL_A": 1,
                "children": [
                    {
                        "PARENT_ID": 4,
                        "CHILD_ID": 11,
                        "NAME": "Masonite",
                        "LEVEL_A": 2,
                        "children": []
                    },
                    {
                        "PARENT_ID": 4,
                        "CHILD_ID": 12,
                        "NAME": "Meat",
                        "LEVEL_A": 2,
                        "children": [
                            {
                                "PARENT_ID": 12,
                                "CHILD_ID": 19,
                                "NAME": "Mutton",
                                "LEVEL_A": 3,
                                "children": []
                            }
                        ]
                    },
                    {
                        "PARENT_ID": 4,
                        "CHILD_ID": 13,
                        "NAME": "Marscapone",
                        "LEVEL_A": 2,
                        "children": [
                            {
                                "PARENT_ID": 13,
                                "CHILD_ID": 14,
                                "NAME": "Malty",
                                "LEVEL_A": 3,
                                "children": [
                                    {
                                        "PARENT_ID": 14,
                                        "CHILD_ID": 17,
                                        "NAME": "Minty",
                                        "LEVEL_A": 4,
                                        "children": [
                                            {
                                                "PARENT_ID": 17,
                                                "CHILD_ID": 16,
                                                "NAME": "spearMint",
                                                "LEVEL_A": 5,
                                                "children": []
                                            },
                                            {
                                                "PARENT_ID": 17,
                                                "CHILD_ID": 18,
                                                "NAME": "pepperMint",
                                                "LEVEL_A": 5,
                                                "children": []
                                            }
                                        ]
                                    }
                                ]
                            },
                            {
                                "PARENT_ID": 13,
                                "CHILD_ID": 15,
                                "NAME": "Minty",
                                "LEVEL_A": 3,
                                "children": []
                            }
                        ]
                    }
                ]
            },
            {
                "PARENT_ID": 1,
                "CHILD_ID": 5,
                "NAME": "Man Power",
                "LEVEL_A": 1,
                "children": [
                    {
                        "PARENT_ID": 5,
                        "CHILD_ID": 20,
                        "NAME": "Manager",
                        "LEVEL_A": 2,
                        "children": []
                    },
                    {
                        "PARENT_ID": 5,
                        "CHILD_ID": 21,
                        "NAME": "Magician",
                        "LEVEL_A": 2,
                        "children": []
                    },
                    {
                        "PARENT_ID": 5,
                        "CHILD_ID": 22,
                        "NAME": "Magister",
                        "LEVEL_A": 2,
                        "children": [
                            {
                                "PARENT_ID": 22,
                                "CHILD_ID": 23,
                                "NAME": "Malpractice",
                                "LEVEL_A": 3,
                                "children": []
                            }
                        ]
                    },
                    {
                        "PARENT_ID": 5,
                        "CHILD_ID": 24,
                        "NAME": "Massage Artist",
                        "LEVEL_A": 2,
                        "children": []
                    }
                ]
            },
            {
                "PARENT_ID": 1,
                "CHILD_ID": 6,
                "NAME": "Measurement",
                "LEVEL_A": 1,
                "children": [
                    {
                        "PARENT_ID": 6,
                        "CHILD_ID": 25,
                        "NAME": "Malleability",
                        "LEVEL_A": 2,
                        "children": []
                    }
                ]
            },
            {
                "PARENT_ID": 1,
                "CHILD_ID": 7,
                "NAME": "Milieu",
                "LEVEL_A": 1,
                "children": [
                    {
                        "PARENT_ID": 7,
                        "CHILD_ID": 26,
                        "NAME": "Marine",
                        "LEVEL_A": 2,
                        "children": []
                    }
                ]
            },
            {
                "PARENT_ID": 1,
                "CHILD_ID": 10,
                "NAME": "Metal Lathe",
                "LEVEL_A": 1,
                "children": []
            }
        ]
    }

But I have no idea how to make it work. I have found a Code in this Post, but I don't know how to apply this to my usecase.

Can anyone help me with this?

I would be very grateful for any advice!

Kind Regards Jegor

2 Answers2

2

First build a map of items by CHILD_ID, then loop the items again and add them to an appropriate parent in the map by PARENT_ID.

And the question assumes there could be only 1 parent:

const map = {};

for(const item of data){
  map[item.CHILD_ID] = {...item, children: []};
}

let result;

for(const item of Object.values(map)){
  map[item.PARENT_ID]?.children.push(item) ?? (result = item);
}

$result.textContent = JSON.stringify(result,0,4);
<script>
const data=[{PARENT_ID:0,CHILD_ID:1,NAME:"Quality",LEVEL_A:0},{PARENT_ID:1,CHILD_ID:2,NAME:"Machine",LEVEL_A:1},{PARENT_ID:1,CHILD_ID:3,NAME:"Method",LEVEL_A:1},{PARENT_ID:1,CHILD_ID:4,NAME:"Material",LEVEL_A:1},{PARENT_ID:1,CHILD_ID:5,NAME:"Man Power",LEVEL_A:1},{PARENT_ID:1,CHILD_ID:6,NAME:"Measurement",LEVEL_A:1},{PARENT_ID:1,CHILD_ID:7,NAME:"Milieu",LEVEL_A:1},{PARENT_ID:1,CHILD_ID:10,NAME:"Metal Lathe",LEVEL_A:1},{PARENT_ID:2,CHILD_ID:3,NAME:"dasd",LEVEL_A:2},{PARENT_ID:2,CHILD_ID:8,NAME:"Mill",LEVEL_A:2},{PARENT_ID:2,CHILD_ID:9,NAME:"Mixer",LEVEL_A:2},{PARENT_ID:4,CHILD_ID:11,NAME:"Masonite",LEVEL_A:2},{PARENT_ID:4,CHILD_ID:12,NAME:"Meat",LEVEL_A:2},{PARENT_ID:4,CHILD_ID:13,NAME:"Marscapone",LEVEL_A:2},{PARENT_ID:5,CHILD_ID:20,NAME:"Manager",LEVEL_A:2},{PARENT_ID:5,CHILD_ID:21,NAME:"Magician",LEVEL_A:2},{PARENT_ID:5,CHILD_ID:22,NAME:"Magister",LEVEL_A:2},{PARENT_ID:5,CHILD_ID:24,NAME:"Massage Artist",LEVEL_A:2},{PARENT_ID:6,CHILD_ID:25,NAME:"Malleability",LEVEL_A:2},{PARENT_ID:7,CHILD_ID:26,NAME:"Marine",LEVEL_A:2},{PARENT_ID:12,CHILD_ID:19,NAME:"Mutton",LEVEL_A:3},{PARENT_ID:13,CHILD_ID:14,NAME:"Malty",LEVEL_A:3},{PARENT_ID:13,CHILD_ID:15,NAME:"Minty",LEVEL_A:3},{PARENT_ID:14,CHILD_ID:17,NAME:"Minty",LEVEL_A:4},{PARENT_ID:17,CHILD_ID:16,NAME:"spearMint",LEVEL_A:5},{PARENT_ID:17,CHILD_ID:18,NAME:"pepperMint",LEVEL_A:5},{PARENT_ID:22,CHILD_ID:23,NAME:"Malpractice",LEVEL_A:3}];
</script>
<div style="white-space:pre;font-family:monospace" id="$result"></div>
Alexander Nenashev
  • 8,775
  • 2
  • 6
  • 17
  • Hi Alexander, thanks for your fast answer. Your Code is similar to that Code i have tried earlier. The Problem there was the leading "[" and ending "]". Is there a way to convert the Array with out them or remove it? – Jegor Wieler Jul 13 '23 at 20:22
  • Good Morning, one last question. What is the meaning of this line in your Code "map[item.PARENT_ID]?.children.push(item) ?? (result = item);". It is like the Ternary Operator. If "map[item.PARENT_ID]" quals to ".children" then push "item"? – Jegor Wieler Jul 14 '23 at 06:50
  • @JegorWieler https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining – Alexander Nenashev Jul 14 '23 at 09:34
2

D3 has a function called d3.stratify that is designed precisely to manipulate tabular data into the tree structure that you need to generate a a tree with D3. Thus, you should be able to do something like so:

let stratified = d3
  .stratify()
  .id((d) => d.CHILD_ID)
  .parentId((d) => d.PARENT_ID)(data);

Note that the root node should have no parent, though. Taking that into account, here it is in action:

data = [
  { PARENT_ID: null, CHILD_ID: 1, NAME: "Quality", LEVEL_A: 0 },
  { PARENT_ID: 1, CHILD_ID: 2, NAME: "Machine", LEVEL_A: 1 },
  { PARENT_ID: 1, CHILD_ID: 3, NAME: "Method", LEVEL_A: 1 },
  { PARENT_ID: 1, CHILD_ID: 4, NAME: "Material", LEVEL_A: 1 },
  { PARENT_ID: 1, CHILD_ID: 5, NAME: "Man Power", LEVEL_A: 1 },
  { PARENT_ID: 1, CHILD_ID: 6, NAME: "Measurement", LEVEL_A: 1 },
  { PARENT_ID: 1, CHILD_ID: 7, NAME: "Milieu", LEVEL_A: 1 },
  { PARENT_ID: 1, CHILD_ID: 10, NAME: "Metal Lathe", LEVEL_A: 1 },
  { PARENT_ID: 2, CHILD_ID: 3, NAME: "dasd", LEVEL_A: 2 },
  { PARENT_ID: 2, CHILD_ID: 8, NAME: "Mill", LEVEL_A: 2 },
  { PARENT_ID: 2, CHILD_ID: 9, NAME: "Mixer", LEVEL_A: 2 },
  { PARENT_ID: 4, CHILD_ID: 11, NAME: "Masonite", LEVEL_A: 2 },
  { PARENT_ID: 4, CHILD_ID: 12, NAME: "Meat", LEVEL_A: 2 },
  { PARENT_ID: 4, CHILD_ID: 13, NAME: "Marscapone", LEVEL_A: 2 },
  { PARENT_ID: 5, CHILD_ID: 20, NAME: "Manager", LEVEL_A: 2 },
  { PARENT_ID: 5, CHILD_ID: 21, NAME: "Magician", LEVEL_A: 2 },
  { PARENT_ID: 5, CHILD_ID: 22, NAME: "Magister", LEVEL_A: 2 },
  { PARENT_ID: 5, CHILD_ID: 24, NAME: "Massage Artist", LEVEL_A: 2 },
  { PARENT_ID: 6, CHILD_ID: 25, NAME: "Malleability", LEVEL_A: 2 },
  { PARENT_ID: 7, CHILD_ID: 26, NAME: "Marine", LEVEL_A: 2 },
  { PARENT_ID: 12, CHILD_ID: 19, NAME: "Mutton", LEVEL_A: 3 },
  { PARENT_ID: 13, CHILD_ID: 14, NAME: "Malty", LEVEL_A: 3 },
  { PARENT_ID: 13, CHILD_ID: 15, NAME: "Minty", LEVEL_A: 3 },
  { PARENT_ID: 14, CHILD_ID: 17, NAME: "Minty", LEVEL_A: 4 },
  { PARENT_ID: 17, CHILD_ID: 16, NAME: "spearMint", LEVEL_A: 5 },
  { PARENT_ID: 17, CHILD_ID: 18, NAME: "pepperMint", LEVEL_A: 5 },
  { PARENT_ID: 22, CHILD_ID: 23, NAME: "Malpractice", LEVEL_A: 3 }
];

let stratified = d3
  .stratify()
  .id((d) => d.CHILD_ID)
  .parentId((d) => d.PARENT_ID)(data);
  
  
d3.select('#viz')
  .append(() => graph(d3.hierarchy(stratified), {
  label: (d) => d.data.data.NAME}))
  
  

// Modified only slightly from
// https://observablehq.com/@d3/d3-hierarchy
function graph(
  root,
  { label = (d) => d.data.id, highlight = () => false, marginLeft = 40 } = {}
) {
  let dx = 12;
  let dy = 120;
  let width = 600;
  let tree = d3
    .tree()
    .separation(() => 3)
    .nodeSize([dx, dy]);
  let treeLink = d3
    .linkHorizontal()
    .x((d) => d.y)
    .y((d) => d.x);
  root = tree(root);

  let x0 = Infinity;
  let x1 = -x0;
  root.each((d) => {
    if (d.x > x1) x1 = d.x;
    if (d.x < x0) x0 = d.x;
  });

  const svg = d3
    .create("svg")
    .attr("viewBox", [0, 0, width, x1 - x0 + dx * 2])
    .style("overflow", "visible");

  const g = svg
    .append("g")
    .attr("font-family", "sans-serif")
    .attr("font-size", 10)
    .attr("transform", `translate(${marginLeft},${dx - x0})`);

  const link = g
    .append("g")
    .attr("fill", "none")
    .attr("stroke", "#555")
    .attr("stroke-opacity", 0.4)
    .attr("stroke-width", 1.5)
    .selectAll("path")
    .data(root.links())
    .join("path")
    .attr("stroke", (d) =>
      highlight(d.source) && highlight(d.target) ? "red" : null
    )
    .attr("stroke-opacity", (d) =>
      highlight(d.source) && highlight(d.target) ? 1 : null
    )
    .attr("d", treeLink);

  const node = g
    .append("g")
    .attr("stroke-linejoin", "round")
    .attr("stroke-width", 3)
    .selectAll("g")
    .data(root.descendants())
    .join("g")
    .attr("transform", (d) => `translate(${d.y},${d.x})`);

  node
    .append("circle")
    .attr("fill", (d) => (highlight(d) ? "red" : d.children ? "#555" : "#999"))
    .attr("r", 2.5);

  node
    .append("text")
    .attr("fill", (d) => (highlight(d) ? "red" : null))
    .attr("stroke", "white")
    .attr("paint-order", "stroke")
    .attr("dy", "0.31em")
    .attr("x", (d) => (d.children ? -6 : 6))
    .attr("text-anchor", (d) => (d.children ? "end" : "start"))
    .text(label);

  return svg.node();
}
<script src="https://d3js.org/d3.v7.min.js"></script>
<div id="viz"></div>
Mark McClure
  • 4,862
  • 21
  • 34