'Loop tree-like structure (id and parent_id)

I have a tree object generated from the DataBase with my data from Excel

I would like loop this object to get the elements like a tree

For example: I would like to display the data in a table, or do a select depending on another select to select an asset

Or do you think I should change my object structure?¿

Excel

Data in excel

Object (from JSON)

[
{"id":"1000","name":"GRUPOS GENERADORES","parent_id":"0"},
{"id":"1100","name":"MOTORES","parent_id":"1000"},
{"id":"1100.1","name":"MOTOR Nº 1","parent_id":"1100"},
{"id":"1100.2","name":"MOTOR Nº 2","parent_id":"1100"},
{"id":"1100.3","name":"MOTOR Nº 3","parent_id":"1100"},
{"id":"1200","name":"SISTEMAS AUXILIARES MOTOR","parent_id":"1000"},
{"id":"1240","name":"INSTRUMENTACIÓN Y CONTROL","parent_id":"1200"},
{"id":"1242","name":"INSTRUMENTACIÓN","parent_id":"1240"},
{"id":"2000","name":"INSTALACIONES AUXILIARES GRUPOS","parent_id":"0"},
{"id":"2100","name":"REFRIGERACIÓN","parent_id":"2000"},
{"id":"2110","name":"AEROREFRIGERADORES","parent_id":"2100"},
{"id":"2110.1","name":"AEROREFRIGERADOR AR1","parent_id":"2110"},
{"id":"2110.2","name":"AEROREFRIGERADOR AR2","parent_id":"2110"},
{"id":"2110.3","name":"AEROREFRIGERADOR AR3","parent_id":"2110"},
{"id":"2110.4","name":"AEROREFRIGERADOR AR4","parent_id":"2110"},
{"id":"2110.5","name":"AEROREFRIGERADOR AR5","parent_id":"2110"}
{"id":"2160","name":"INSTRUMENTACIÓN","parent_id":"2100"},
{"id":"2160.1","name":"CAUDALÍMETRO AT","parent_id":"2160"},
{"id":"2160.2","name":"CAUDALÍMETRO BT","parent_id":"2160"},
]

I made this method to try to get the child elements from a given name parent, but if i call the method with a name that is duplicated, this method doesn't work.

function filter_by(filter) {
    const id = data.filter(i => (i.name === filter))[0].id
    const data_filtered = data.filter(i => (i.parent_id === id))
    return data_filtered
}
console.log(filter_by("AEROREFRIGERADORES"))
// this throws an error 
// console.log(filter_by("INSTRUMENTACIÓN"))
<script>
const data = [
{"id":"1000","name":"GRUPOS GENERADORES","parent_id":"0"},
{"id":"1100","name":"MOTORES","parent_id":"1000"},
{"id":"1100.1","name":"MOTOR Nº 1","parent_id":"1100"},
{"id":"1100.2","name":"MOTOR Nº 2","parent_id":"1100"},
{"id":"1100.3","name":"MOTOR Nº 3","parent_id":"1100"},
{"id":"2000","name":"INSTALACIONES AUXILIARES GRUPOS","parent_id":"0"},
{"id":"2100","name":"REFRIGERACIÓN","parent_id":"2000"},
{"id":"2110","name":"AEROREFRIGERADORES","parent_id":"2100"},
{"id":"2110.1","name":"AEROREFRIGERADOR AR1","parent_id":"2110"},
{"id":"2110.2","name":"AEROREFRIGERADOR AR2","parent_id":"2110"},
{"id":"2110.3","name":"AEROREFRIGERADOR AR3","parent_id":"2110"},
{"id":"2110.4","name":"AEROREFRIGERADOR AR4","parent_id":"2110"},
{"id":"2110.5","name":"AEROREFRIGERADOR AR5","parent_id":"2110"}
];
</script>


Solution 1:[1]

You could get the children first by parent_id and then find the parent item and return the children of this node.

const
    find = (data, name) => {
        const
            children = data.reduce((r, o) => ((r[o.parent_id] ??= []).push(o), r), {});

        return children[data.find(o => o.name === name).id];
    },
    data = [{ id: "1000", name: "GRUPOS GENERADORES", parent_id: "0" }, { id: "1100", name: "MOTORES", parent_id: "1000" }, { id: "1100.1", name: "MOTOR Nº 1", parent_id: "1100" }, { id: "1100.2", name: "MOTOR Nº 2", parent_id: "1100" }, { id: "1100.3", name: "MOTOR Nº 3", parent_id: "1100" }, { id: "2000", name: "INSTALACIONES AUXILIARES GRUPOS", parent_id: "0" }, { id: "2100", name: "REFRIGERACIÓN", parent_id: "2000" }, { id: "2110", name: "AEROREFRIGERADORES", parent_id: "2100" }, { id: "2110.1", name: "AEROREFRIGERADOR AR1", parent_id: "2110" }, { id: "2110.2", name: "AEROREFRIGERADOR AR2", parent_id: "2110" }, { id: "2110.3", name: "AEROREFRIGERADOR AR3", parent_id: "2110" }, { id: "2110.4", name: "AEROREFRIGERADOR AR4", parent_id: "2110" }, { id: "2110.5", name: "AEROREFRIGERADOR AR5", parent_id: "2110" }],
    result = find(data, 'AEROREFRIGERADORES');

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

Solution 2:[2]

You could:

  • Create a nested forest structure from the flat array input
  • Create a 2D array from that tree
  • Create a HTML table from that 2D array (as example output)

Here are three functions for doing that:

function toForest(data, topLevelParentId="0") {
    let children = []; // root(s) of the forest to populate 
    let map = new Map(data.map(({id, name, parent_id}) => [id, {id, name, children: [] }]))
             .set(topLevelParentId, { children }); // Create an entry to hold all the roots
    for (let {id, parent_id} of data) map.get(parent_id).children.push(map.get(id));
    return children;
}

function toMatrix(forest, depth=0) {
    let table = (forest ?? []).flatMap(({id, name, children}) =>
        [[id, name], ...toMatrix(children, depth + 1).map(row => ["", ...row])]
    );
    // Pad rows at the right, so they are all of equal length
    let maxWidth = Math.max(...table.map(row => row.length));
    return table.map(row => row.concat(
        ...Array.from({length: maxWidth - row.length}, () => ""))
    );
}

function toHTML(matrix) {
    return `<table>${matrix.map(row => 
        `<tr>${row.map(cell => `<td>${cell}</td>`).join("")}</td>`
    ).join("\n")}</table>`;
}


// demo
let data = [{"id":"1000","name":"GRUPOS GENERADORES","parent_id":"0"},{"id":"1100","name":"MOTORES","parent_id":"1000"},{"id":"1100.1","name":"MOTOR Nº 1","parent_id":"1100"},{"id":"1100.2","name":"MOTOR Nº 2","parent_id":"1100"},{"id":"1100.3","name":"MOTOR Nº 3","parent_id":"1100"},{"id":"1200","name":"SISTEMAS AUXILIARES MOTOR","parent_id":"1000"},{"id":"1240","name":"INSTRUMENTACIÓN Y CONTROL","parent_id":"1200"},{"id":"1242","name":"INSTRUMENTACIÓN","parent_id":"1240"},{"id":"2000","name":"INSTALACIONES AUXILIARES GRUPOS","parent_id":"0"},{"id":"2100","name":"REFRIGERACIÓN","parent_id":"2000"},{"id":"2110","name":"AEROREFRIGERADORES","parent_id":"2100"},{"id":"2110.1","name":"AEROREFRIGERADOR AR1","parent_id":"2110"},{"id":"2110.2","name":"AEROREFRIGERADOR AR2","parent_id":"2110"},{"id":"2110.3","name":"AEROREFRIGERADOR AR3","parent_id":"2110"},{"id":"2110.4","name":"AEROREFRIGERADOR AR4","parent_id":"2110"},{"id":"2110.5","name":"AEROREFRIGERADOR AR5","parent_id":"2110"},{"id":"2160","name":"INSTRUMENTACIÓN","parent_id":"2100"},{"id":"2160.1","name":"CAUDALÍMETRO AT","parent_id":"2160"},{"id":"2160.2","name":"CAUDALÍMETRO BT","parent_id":"2160"},];

let forest = toForest(data);    // nested structure
let matrix = toMatrix(forest);  // 2D array
let html = toHTML(matrix);      // <table>
document.getElementById("container").innerHTML = html;
table { border-collapse: collapse }
td { border: 1px solid; font-size: smaller }
<div id="container"></div>

For selection, you would find the node first in the forest , and then pass the found node to the toMatrix function.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 trincot