I'm trying to recursively delete list items based on their nested content (delete if checked = TRUE
).
I recently asked a similar question here. However, I just realized that the given answer doesn't work recursively.
Here is my example data:
library(jsTreeR)
library(jsonlite)
nodes <- list(
list(
text = "Branch 1",
state = list(
opened = TRUE,
disabled = FALSE,
selected = FALSE
),
type = "parent",
children = list(
list(
text = "Leaf A",
state = list(
opened = TRUE,
disabled = FALSE,
selected = FALSE,
checked = FALSE
),
type = "child"
),
list(
text = "Leaf B",
state = list(
opened = TRUE,
disabled = FALSE,
selected = FALSE,
checked = FALSE
),
type = "child"
),
list(
text = "Leaf C",
state = list(
opened = TRUE,
disabled = FALSE,
selected = FALSE,
checked = TRUE
),
type = "child"
),
list(
text = "Leaf D",
state = list(
opened = TRUE,
disabled = FALSE,
selected = FALSE,
checked = FALSE
),
type = "child",
children = list(
list(
text = "Leaf D1",
state = list(
opened = TRUE,
disabled = FALSE,
selected = FALSE,
checked = FALSE
),
type = "child"
),
list(
text = "Leaf D2",
state = list(
opened = TRUE,
disabled = FALSE,
selected = FALSE,
checked = TRUE
),
type = "child"
)
)
)
)
),
list(
text = "Branch 2",
type = "parent",
state = list(
opened = TRUE,
disabled = FALSE,
selected = FALSE,
checked = FALSE
)
)
)
jstree(nodes, checkboxes = TRUE, checkWithText = FALSE)
# toJSON(nodes, pretty = TRUE)
I tried using base R and {rrapply}, but can't get it to work properly:
delete_unchecked <- function(x) {
for (i in seq_along(x)) {
value <- x[[i]]
if (is.list(value)) {
x[[i]] <- delete_unchecked(value)
} else {
if("state" %in% names(x) && isTRUE(x$state$checked)){x[[i]] <- NULL}
}
}
x
}
library(rrapply)
result <- rrapply(nodes, condition = function(x){
if("state" %in% names(x)){
isTRUE(x$state$checked)
} else {TRUE}
}, how = "prune")
This is my expected output:
expected_output <- list(
list(
text = "Branch 1",
state = list(
opened = TRUE,
disabled = FALSE,
selected = FALSE
),
type = "parent",
children = list(
list(
text = "Leaf A",
state = list(
opened = TRUE,
disabled = FALSE,
selected = FALSE,
checked = FALSE
),
type = "child"
),
list(
text = "Leaf B",
state = list(
opened = TRUE,
disabled = FALSE,
selected = FALSE,
checked = FALSE
),
type = "child"
),
list(
text = "Leaf D",
state = list(
opened = TRUE,
disabled = FALSE,
selected = FALSE,
checked = FALSE
),
type = "child",
children = list(
list(
text = "Leaf D1",
state = list(
opened = TRUE,
disabled = FALSE,
selected = FALSE,
checked = FALSE
),
type = "child"
)
)
)
)
),
list(
text = "Branch 2",
type = "parent",
state = list(
opened = TRUE,
disabled = FALSE,
selected = FALSE,
checked = FALSE
)
)
)
PS: I'd prefer base R answers - however, open to other suggestions.
You can use some recursion to check each node and it's children
remove_checked <- function(x) {
drop_null <- function(x) Filter(Negate(is.null), x)
is_checked <- function(x) "state" %in% names(x) && "checked" %in% names(x$state) && x$state$checked
drop <- function(x) {
if (is_checked(x)) return(NULL)
if ("children" %in% names(x)) {
x$children <- drop_null(lapply(x$children, drop))
}
x
}
drop_null(lapply(x, drop))
}
result <- remove_checked(nodes)
We swap out the checked ones with NULL and then remove NULL from the list.