How to turn a data frame into a recursive list of lists (tree)?
Input
roles_data <- data.frame(
Child = c("A", "B", "C", "D", "E", "F"),
ParentID = c(NA, "A", "B", "B", "D", "D") # The top-level role has no parent (NA)
)
Desired Output
desired_output <- list(
list(
text = "A",
children = list(
list(
text = "B",
children = list(
list(
text = "C"),
list(
text = "D"
,
children = list(
list(
text = "E"
),
list(
text = "F"
) ) ) ) ) ) )
(Hopefully, I did not mess with the output table at the bottom, just need to have more than one level of depth.)
You're describing a tree structure: each parent can have multiple children, but each child has exactly one parent. Searching for tree questions, this is similar in principle to Transform a dataframe into a tree structure list of lists, in the sense that we can start from the root and recurse over children. However, as your input and output data are in a different format, we can do something a little more ergonomic. Basically, for each node, create a list with text = node_name
, and if there are children, recursively build a subtree for them.
generate_tree <- function(dat, root = dat$Child[is.na(dat$ParentID)]) {
node <- list(text = root)
children <- na.omit(dat$Child[dat$ParentID == root])
if (length(children) > 0) {
node$children <- lapply(children, \(child) generate_tree(dat, child))
}
node
}
If you really want the outer list()
you can wrap it in one:
identical(list(generate_tree(roles_data)), desired_output)
# [1] TRUE
And here is the output as json for printing purposes:
generate_tree(roles_data) |> jsonlite::toJSON(pretty = TRUE, auto_unbox = TRUE)
{
"text": "A",
"children": [
{
"text": "B",
"children": [
{
"text": "C"
},
{
"text": "D",
"children": [
{
"text": "E"
},
{
"text": "F"
}
]
}
]
}
]
}