This produces the intended structure, but is obviously not generic:
> list(list(RECX5[[1]][[1]],RECX5[[2]][[1]]),
+ list(list(RECX5[[1]][[2]],RECX5[[2]][[2]]),
+ list(RECX5[[1]][[3]],RECX5[[2]][[3]])))
[[1]]
[[1]][[1]]
[1] 1 2 3 4 5
[[1]][[2]]
[1] -11 -12 -13 -14 -15
[[2]]
[[2]][[1]]
[[2]][[1]][[1]]
[1] 6 7 8 9 10
[[2]][[1]][[2]]
[1] 16 17 18 19 20
[[2]][[2]]
[[2]][[2]][[1]]
[1] 11 12 13 14 15
[[2]][[2]][[2]]
[1] 21 22 23 24 25
What do you want more generally? Generally speaking [sorry], you can generalize the above by just picking an indexing level and then lapply
ing some range of indices function(x) [that expression]
where the multiple calls over the index level you want to functionalize are replaced with x
.
EDIT/UPDATE
Per my several comments I don't understand how you think you're generically specifying input to your solution, but I've spelled out an approach here that should indicate what the requirements are for a solution. Ideally, you can simplify this strategy if you actually do have some assumptions that hold that I haven't hard-coded in.
Note, yes, this solution is still not generic in handling result lists of arbitrary depth. That is doable, but an even bigger mess.
First: let's walk the list to build its structure as a table. This will make it easier to traverse the list in the eventual function call:
library(data.table)
build.structure <- function(src) {
rbindlist(lapply(1:length(src),
function(x) data.table(input.position.depth.0=rep(x,length(src[[x]])),
input.position.depth.1=1:length(src[[x]]))))
}
struct <- build.structure(RECX5)
> struct
input.position.depth.0 input.position.depth.1
1: 1 1
2: 1 2
3: 1 3
4: 2 1
5: 2 2
6: 2 3
Now manually build the desired result structure that you'll pass to the function. This is where I don't understand what you've tried to do. Somehow the result function needs all this information: where will each element from the input get put precisely in the list structure of the output? I've made this excruciatingly specific in this example, but if you have simplifying assumptions, you'll be able to skip some of this detail:
struct[,result.position.depth.0:=c( 1, 2, 2, 1, 2, 2)]
struct[,result.position.depth.1:=c( 1, 1, 2, 2, 1, 2)]
struct[,result.position.depth.2:=c(NA, 1, 1, NA, 2, 2)]
Note reading down this text in a column is the resulting list position for the element; when you display the table you read across:
> struct
input.position.depth.0 input.position.depth.1 result.position.depth.0 result.position.depth.1
1: 1 1 1 1
2: 2 1 1 2
3: 1 2 2 1
4: 2 2 2 1
5: 1 3 2 2
6: 2 3 2 2
result.position.depth.2
1: NA
2: NA
3: 1
4: 2
5: 1
6: 2
The function I write below assumes the new list is built in dictionary order of elements via depth indexing, so sort this table accordingly:
setkey(struct,result.position.depth.0,
result.position.depth.1,
result.position.depth.2)
(Note, there is a breakdown in generality here. If you need this to go even deeper than 2 levels, melt
the data and add an additional depth column to refer to the depth as that column's value, and modify below accordingly to pluck the depth out rather than hard-coding the intended depth.)
Now a convenience function to extract data from the input list as specified by the row in the table we just built:
extract <- function(src,struct,a.row) {
src[[struct[a.row,input.position.depth.0]]][[struct[a.row,input.position.depth.1]]]
}
Now the long part.
First make a global object. (This just means we don't have to worry about references. This can be avoided, but I'm not going to bother.)
result <<- list()
Then let's use a function that modifies this object [see comments inline]
get.lists.from.structure <- function(src, struct) {
lapply(1:nrow(struct), function(the.row) {
#Extract the value that we'll insert next
val <- extract(src,struct,the.row)
#If there's not already a slot at the top level for this value,
#make an empty list there so we'll have a place to put it.
#Without this you'll get subscript out of bounds errors
if(length(result) < struct[the.row,result.position.depth.0]) {
result[[struct[the.row,result.position.depth.0]]] <<- list()
}
#Now if the resulting object is getting stuck one level down,
#take the first branch; just put it there.
if(struct[the.row,is.na(result.position.depth.2)]) {
result[[struct[the.row,result.position.depth.0
]]][[struct[the.row,result.position.depth.1]]] <<- val
} else {
#otherwise, take this branch, but WAIT! Repeat the same check;
#if there's not already a slot at this depth, put
#an empty list in
if(length(result[[struct[the.row,result.position.depth.0]]]) <
struct[the.row,result.position.depth.1]) {
result[[struct[the.row,result.position.depth.0
]]][[struct[the.row,result.position.depth.1]]] <<- list()
}
#then put the value in at its intended position.
result[[struct[the.row,result.position.depth.0
]]][[struct[the.row,result.position.depth.1
]]][[struct[the.row,result.position.depth.2]]] <<- val
}
})
}
OK, now we can run this (ignore what it prints, or add an invisible
)
get.lists.from.structure(RECX5,struct)
And check that it had the correct side effect on result
:
> result
[[1]]
[[1]][[1]]
[1] 1 2 3 4 5
[[1]][[2]]
[1] -11 -12 -13 -14 -15
[[2]]
[[2]][[1]]
[[2]][[1]][[1]]
[1] 6 7 8 9 10
[[2]][[1]][[2]]
[1] 16 17 18 19 20
[[2]][[2]]
[[2]][[2]][[1]]
[1] 11 12 13 14 15
[[2]][[2]][[2]]
[1] 21 22 23 24 25