Search code examples
rlistvectoraggregate

R - Collapse into vector same member of a list


I have a list with same structure for every member as the following

config <- NULL

config[["secA"]] <- NULL
config[["secA"]]$VAL <- 0
config[["secA"]]$ARR <- c(1,2,3,4,5)
config[["secA"]]$DF  <- data.frame(matrix(c(1,5,3,8),2,2))

config[["secB"]] <- NULL
config[["secB"]]$VAL <- 1
config[["secB"]]$ARR <- c(1,3,2,4,9)
config[["secB"]]$DF  <- data.frame(matrix(c(2,6,1,9),2,2))

config[["secC"]] <- NULL
config[["secC"]]$VAL <- 5
config[["secC"]]$ARR <- c(4,2,1,5,8)
config[["secC"]]$DF  <- data.frame(matrix(c(4,2,1,7),2,2))

and I need to obtain 3 vectors VAL, ARR and DF, each with the concatenated elements of the corresponding member. such as

# VAL: 0,1,5
# ARR: 1,2,3,4,5,1,3,2,4,9,4,2,1,5,8
# DF:  1,5,3,8,2,6,1,9,4,2,1,7

Looking at similar situations, I have the feeling I need to use a combination of do.call and cbind or lapply but I have no clue. any suggestions?


Solution

  • config <- NULL
    
    config[["secA"]] <- NULL
    config[["secA"]]$VAL <- 0
    config[["secA"]]$ARR <- c(1,2,3,4,5)
    config[["secA"]]$DF  <- data.frame(matrix(c(1,5,3,8),2,2))
    
    config[["secB"]] <- NULL
    config[["secB"]]$VAL <- 1
    config[["secB"]]$ARR <- c(1,3,2,4,9)
    config[["secB"]]$DF  <- data.frame(matrix(c(2,6,1,9),2,2))
    
    config[["secC"]] <- NULL
    config[["secC"]]$VAL <- 5
    config[["secC"]]$ARR <- c(4,2,1,5,8)
    config[["secC"]]$DF  <- data.frame(matrix(c(4,2,1,7),2,2))
    
    sapply(names(config[[1]]), function(x)
      unname(unlist(sapply(config, `[`, x))), USE.NAMES = TRUE)
    
    # $VAL
    # [1] 0 1 5
    # 
    # $ARR
    # [1] 1 2 3 4 5 1 3 2 4 9 4 2 1 5 8
    # 
    # $DF
    # [1] 1 5 3 8 2 6 1 9 4 2 1 7
    

    Or you can use this clist function

    Unfortunately there were no other answers.

    (l <- Reduce(clist, config))
    # $VAL
    # [1] 0 1 5
    # 
    # $ARR
    # [1] 1 2 3 4 5 1 3 2 4 9 4 2 1 5 8
    # 
    # $DF
    #   X1 X2 X1 X2 X1 X2
    # 1  1  3  2  1  4  1
    # 2  5  8  6  9  2  7
    

    It merges data frames and matrices, so you need to unlist to get the vector you want

    l$DF <- unname(unlist(l$DF))
    l
    # $VAL
    # [1] 0 1 5
    # 
    # $ARR
    # [1] 1 2 3 4 5 1 3 2 4 9 4 2 1 5 8
    # 
    # $DF
    # [1] 1 5 3 8 2 6 1 9 4 2 1 7
    

    Function

    clist <- function (x, y) {
      islist <- function(x) inherits(x, 'list')
      '%||%' <- function(a, b) if (!is.null(a)) a else b
      get_fun <- function(x, y)
        switch(class(x %||% y),
               matrix = cbind,
               data.frame = function(x, y)
                 do.call('cbind.data.frame', Filter(Negate(is.null), list(x, y))),
               factor = function(...) unlist(list(...)), c)
    
      stopifnot(islist(x), islist(y))
      nn <- names(rapply(c(x, y), names, how = 'list'))
      if (is.null(nn) || any(!nzchar(nn)))
        stop('All non-NULL list elements should have unique names', domain = NA)
    
      nn <- unique(c(names(x), names(y)))
      z <- setNames(vector('list', length(nn)), nn)
    
      for (ii in nn)
        z[[ii]] <- if (islist(x[[ii]]) && islist(y[[ii]]))
          Recall(x[[ii]], y[[ii]]) else
            (get_fun(x[[ii]], y[[ii]]))(x[[ii]], y[[ii]])
      z
    }