Search code examples
rpurrr

How to flatten list with heterogeneous sublists


Starting from init_list or list_clustered_by_name I want to arrive to desired_list. I wonder if there is a better procedure, than the one I developed, here:

Maybe something with map_if, checking length, but I couldn't.

library(purrr)

cluster_by_name <- function (y) {
  map(unique(names(y)), ~ paste(unique(y[which(names(y)==.)]), collapse=", ")) %>% 
  setNames(unique(names(y)))
}

flatten_sublist <- function(x, colnames) {
  for (colname in colnames){
    x <- append(x, unlist(x[[colname]]))
    x[[colname]] <- NULL
  }
  x
}

vec <- c("A"=1,"B"=2, "A"=1, "B"=4)
vec2 <- c("A2"=1,"B2"=2, "A2"=3, "B2"=4)

init_list <- list(vec=vec, C=1, vec2=vec2)

init_list # starting point

list_clustered_by_name <- init_list %>% 
  map_if(function(x) length(x) > 1,
    function (y) cluster_by_name(y)
  )

list_clustered_by_name # possible starting point
# $vec
# $vec$A
# [1] "1"
# $vec$B
# [1] "2, 4"

# $C
# [1] 1

# $vec2
# $vec2$A2
# [1] "1, 3"
# $vec2$B2
# [1] "2, 4"

composed <- keep(list_clustered_by_name, ~(length(.) > 1), ~.)
single_features <- keep(list_clustered_by_name, ~(length(.) == 1), ~.)

composed_flattened <- flatten_sublist(composed, names(composed))

desired_list <- c(composed_flattened, single_features)

desired_list
# $A
# [1] "1"

# $B
# [1] "2, 4"

# $A2
# [1] "1, 3"

# $B2
# [1] "2, 4"

# $C
# [1] 1

Solution

  • This might work for you:

    library(purrr)
    
    map_if(init_list, ~ length(.x) > 1, ~ as.list(tapply(.x, names(.x), \(y) toString(unique(y))))) |>
      list_flatten(name_spec = "{inner}")
    
    $A
    [1] "1"
    
    $B
    [1] "2, 4"
    
    $C
    [1] 1
    
    $A2
    [1] "1, 3"
    
    $B2
    [1] "2, 4"