I have a flatlist with hierarchy structure represented by ID parentID relationship like so:
[
{ id: "100", bezeichnung: "Node 1", parentId: null },
{ id: "101", bezeichnung: "Node 1 A", parentId: "100" },
{ id: "102", bezeichnung: "Node 1 B", parentId: "100" },
{ id: "200", bezeichnung: "Node 2", parentId: null },
{ id: "201", bezeichnung: "Node 2 A", parentId: "200" },
{ id: "202", bezeichnung: "Node 2 B", parentId: "200" },
{ id: "204", bezeichnung: "Node 2 BA", parentId: "202" },
{ id: "203", bezeichnung: "Node 3 A", parentId: "200" }
]
I like to translate this into something like this:
[
{ id: "100", bezeichnung: "Node 1", parentId: null, indent: 0 },
{ id: "101", bezeichnung: "Node 1 A", parentId: "100", indent: 1 },
{ id: "102", bezeichnung: "Node 1 B", parentId: "100", indent: 1 },
{ id: "200", bezeichnung: "Node 2", parentId: null, indent: 0 },
{ id: "201", bezeichnung: "Node 2 A", parentId: "200", indent: 1 },
{ id: "202", bezeichnung: "Node 2 B", parentId: "200", indent: 1 },
{ id: "204", bezeichnung: "Node 2 BA", parentId: "202", indent: 2 },
{ id: "203", bezeichnung: "Node 3 A", parentId: "200", indent: 1 }
]
note that the above shown Ids might be arbitrary uuids and not 0,100,200 etc.
CodePudding user response:
So what you have is a list of nodes of a tree. Tree traversal is often most intuitively done using recursion:
let input = [
{ id: "100", bezeichnung: "Node 1", parentId: null },
{ id: "101", bezeichnung: "Node 1 A", parentId: "100" },
{ id: "102", bezeichnung: "Node 1 B", parentId: "100" },
{ id: "200", bezeichnung: "Node 2", parentId: null },
{ id: "201", bezeichnung: "Node 2 A", parentId: "200" },
{ id: "202", bezeichnung: "Node 2 B", parentId: "200" },
{ id: "204", bezeichnung: "Node 2 BA", parentId: "202" },
{ id: "203", bezeichnung: "Node 3 A", parentId: "200" }
];
input.forEach(element => element.indent = recursive(element,0))
function recursive (element, i) {
if (!element.parentId) return i;
return recursive(input.find(parent => parent.id === element.parentId), i 1)
}
console.log(input)
CodePudding user response:
If you want a non-mutating version (after all, we're not barbarians, right?!), you could try something like this:
const depths = (nodes, parentId = null, depth = 0) =>
nodes .filter (n => n.parentId === parentId)
.flatMap (({id}) => [[id, depth], ...depths (nodes, id, depth 1)])
const addIndents = (nodes, indents = Object .fromEntries (depths (nodes))) =>
nodes .map (node => ({...node, indent: indents [node .id]}))
const nodes = [{id: "100", bezeichnung: "Node 1", parentId: null}, {id: "101", bezeichnung: "Node 1 A", parentId: "100"}, {id: "102", bezeichnung: "Node 1 B", parentId: "100"}, {id: "200", bezeichnung: "Node 2", parentId: null}, {id: "201", bezeichnung: "Node 2 A", parentId: "200"}, {id: "202", bezeichnung: "Node 2 B", parentId: "200"}, {id: "204", bezeichnung: "Node 2 BA", parentId: "202"}, {id: "203", bezeichnung: "Node 3 A", parentId: "200"}]
console .log ('new list: ', addIndents (nodes))
console .log ('unmodified original: ', nodes)
.as-console-wrapper {max-height: 100% !important; top: 0}
Here we first calculate depths
, which looks something like this:
[["100", 0], ["101", 1], ["102", 1], ["200", 0], ["201", 1], ["202", 1], ["204", 2], ["203", 1]]
then use fromEntries
to convert that into an Object like this:
{"100": 0, "101": 1, "102": 1, "200": 0, "201": 1, "202": 1, "203": 1, "204": 2}
Then we map
over the input, creating new nodes by shallow-cloning the old ones and adding an indent
property.
I would suggest that if you're storing that property in something like raw data, that it might better be called depth
rather than indent
. Even if you're currently only planning on using it for indentation, depth
is more generic and might apply to many more situations.