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)