Home > front end >  appending elements of list to the next element of that list
appending elements of list to the next element of that list

Time:05-11

Imagine I'm working with a list of lists, as follows:

lol <- list(l1 = list(a = c(1:3), b = c(4:6), c = 2),
            l2 = list(a = c(1:3), b = c(4:6)),
            l3 = list(a = c(1:3), b = c(4:6)))

To which I want to apply a function, say:

foo <- function(x){
  sum(x$a, x$b)*x$c
}

The element c resulting from the first list needs to be appended to the next element in the list (l2) in order for the function to work. So the algorithm is:

A) Calculate sum(a, b) multiply by c -> results in newc.

B) Append newc to the next list, calling it c

C) Repeat

I know how to do this in a loop, as follows:

for(i in 1:length(lol)){
    s <- lol[[i]]
    s[["newc"]] <- foo(s)
    lol[[i]] <- list()
    lol[[i]] <- s
    if((i 1)<(length(lol) 1)){
      lol[[i 1]][["c"]] <- lol[[i]][["newc"]]
    }
} 

For my case, I have to do this operation with a lot more data (more lists within the list) and iterated a lot of times (minimum 100) which gives me high runtimes (about 2s in some trials). As I need to use this with increasingly massive data, I would like to have alternatives to this loop.

EDIT: Thank you for your answers. I need to clarify though, that I need both c and newc in my lists. It wasn't clear before. Thank you.

Do you have any ideas on how to solve this without the loop? Thanks!

CodePudding user response:

The problem can be rephrased as to update the current element of the list based on the previous element of the list.
The loop can be simplified to a one-liner:

lol <- list(l1 = list(a = c(1:3), b = c(4:6), c = 2),
            l2 = list(a = c(1:3), b = c(4:6)),
            l3 = list(a = c(1:3), b = c(4:6)))

foo <- function(x){
  sum(x$a, x$b)*x$c
}

for(i in seq_along(lol)[-1]){
  lol[[i]][["c"]] <- foo(lol[[i - 1L]])
} 

lol
#> $l1
#> $l1$a
#> [1] 1 2 3
#> 
#> $l1$b
#> [1] 4 5 6
#> 
#> $l1$c
#> [1] 2
#> 
#> 
#> $l2
#> $l2$a
#> [1] 1 2 3
#> 
#> $l2$b
#> [1] 4 5 6
#> 
#> $l2$c
#> [1] 42
#> 
#> 
#> $l3
#> $l3$a
#> [1] 1 2 3
#> 
#> $l3$b
#> [1] 4 5 6
#> 
#> $l3$c
#> [1] 882

Created on 2022-05-11 by the reprex package (v2.0.1)


Edit

Here is another solution, based on Ritchie Sacramento's comment.

lol <- list(l1 = list(a = c(1:3), b = c(4:6), c = 2),
            l2 = list(a = c(1:3), b = c(4:6)),
            l3 = list(a = c(1:3), b = c(4:6)))

foo <- function(x, y) {
  sum(y$a, y$b)*x
}

newc <- Reduce(foo, lol[-1], init = lol[[1]][["c"]], accumulate = TRUE)
Map(\(x, newc) {x[["c"]] <- newc; x}, lol, newc)
#> $l1
#> $l1$a
#> [1] 1 2 3
#> 
#> $l1$b
#> [1] 4 5 6
#> 
#> $l1$c
#> [1] 2
#> 
#> 
#> $l2
#> $l2$a
#> [1] 1 2 3
#> 
#> $l2$b
#> [1] 4 5 6
#> 
#> $l2$c
#> [1] 42
#> 
#> 
#> $l3
#> $l3$a
#> [1] 1 2 3
#> 
#> $l3$b
#> [1] 4 5 6
#> 
#> $l3$c
#> [1] 882

Created on 2022-05-11 by the reprex package (v2.0.1)

CodePudding user response:

I wouldn't use a loop:

lol <- lapply(lol, \(x) c(x, c=lol[[1]]$c))
lapply(lol, foo)
  • Related