Create list of lists in R - r

I am new in R and I have a task to create list of lists.
I tried this:
newl <- list()
newl <- append(newl, list(a = 1, b = "x"))
newl <- append(newl, list(a = 15, b = "y"))
newl <- append(newl, list(a = 10, b = "z"))
But append works like extend Python function:
$a
[1] 1
$b
[1] "x"
$a
[1] 15
$b
[1] "y"
$a
[1] 10
$b
[1] "z"
I want to get something like this:
[[1]]
$a
[1] 1
$b
[1] "x"
[[2]]
$a
[1] 15
$b
[1] "y"
[[3]]
$a
[1] 10
$b
[1] "z"
Also I want to have an opportunity to sort elements of my list of lists by parameter (for example, by a). It would look like this:
[[1]]
$a
[1] 1
$b
[1] "x"
[[2]]
$a
[1] 10
$b
[1] "z"
[[3]]
$a
[1] 15
$b
[1] "y"
Also it's important for me to have elements different types inside lists (int and string in the example). In real task it would be function, vector, double and matrix.
Maybe I need to choose another data type to solve my problem?

Actually you are already close to your desired output, but you may need another list() within append, e.g.,
newl <- list()
newl <- append(newl, list(list(a = 1, b = "x")))
newl <- append(newl, list(list(a = 15, b = "y")))
newl <- append(newl, list(list(a = 10, b = "z")))
such that
> newl
[[1]]
[[1]]$a
[1] 1
[[1]]$b
[1] "x"
[[2]]
[[2]]$a
[1] 15
[[2]]$b
[1] "y"
[[3]]
[[3]]$a
[1] 10
[[3]]$b
[1] "z"
If you want to sort by $a, you can try
> newl[order(sapply(newl, `[[`, "a"))]
[[1]]
[[1]]$a
[1] 1
[[1]]$b
[1] "x"
[[2]]
[[2]]$a
[1] 10
[[2]]$b
[1] "z"
[[3]]
[[3]]$a
[1] 15
[[3]]$b
[1] "y"

We could do a split
lst1 <- unname(split(newl, as.integer(gl(length(newl), 2, length(newl)))))

Related

How to unlist nested lists while keeping vectors

I'd like to unlist a nested list with has some items as vectors. The problem is that unlist also splits up these vectors. How can I keep them as single items?
a) one level up (unlist parameter: recursive = F)
b) all levels (unlist parameter: recursive = T)
Here's the example:
list0 <- list(c(1,2),
list(3,
c(4,5)
)
)
> list0
[[1]]
[1] 1 2
[[2]]
[[2]][[1]]
[1] 3
[[2]][[2]]
[1] 4 5
If we unlist one level:
list1 <- unlist(list0, recursive = F)
we get:
> list1
[[1]]
[1] 1
[[2]]
[1] 2
[[3]]
[1] 3
[[4]]
[1] 4 5
but, as I'd like to keep vectors as they are, I'd like to get:
[[1]]
[1] 1 2
[[2]]
[1] 3
[[3]]
[1] 4 5
Maybe one way is with a for loop, but I guess that would be slow if the number of lists is high.
Could anyone give me some hints, please?
Thanks in advance
For your example, the code below gives the expected result.
f <- function(x){
if(is.atomic(x)){
list(x)
}else{
x
}
}
unlist(lapply(list0, f), recursive=FALSE)
But perhaps you need something which works with more nested levels, like:
f <- function(x){
if(is.atomic(x)){
list(x)
}else{
x
}
}
g <- function(L){
out <- unlist(lapply(L, f), recursive=FALSE)
while(any(sapply(out, is.list))){
out <- g(out)
}
out
}
list1 <- list(c(1,2),
list(3, c(4,5)),
list(6, list(c(7,8)))
)
list1_flattened <- g(list1)
which gives:
> list1
[[1]]
[1] 1 2
[[2]]
[[2]][[1]]
[1] 3
[[2]][[2]]
[1] 4 5
[[3]]
[[3]][[1]]
[1] 6
[[3]][[2]]
[[3]][[2]][[1]]
[1] 7 8
> list1_flattened
[[1]]
[1] 1 2
[[2]]
[1] 3
[[3]]
[1] 4 5
[[4]]
[1] 6
[[5]]
[1] 7 8

Convert or transform the list

Input list is:
$A
[1] 25
$B
[1] 22
$C
[1] 25
$D
[1] 26
----
Need to convert this to
$25
[1] "A" "C"
$22
[1] "B"
$26
[1] "D"
How do I change the grouping? Please help me.
If your list is called "L" (example below), try:
L <- list(A = 25, B = 22, C = 25, D = 26)
split(names(L), unlist(L))
# $`22`
# [1] "B"
#
# $`25`
# [1] "A" "C"
#
# $`26`
# [1] "D"
You could also try with(stack(L), split(as.character(ind), values)).

R: relisting a flat list

This question has a nice solution of flattening lists while preserving their data types (which unlist does not):
flatten = function(x, unlist.vectors=F) {
while(any(vapply(x, is.list, logical(1)))) {
if (! unlist.vectors)
x = lapply(x, function(x) if(is.list(x)) x else list(x))
x = unlist(x, recursive=F)
}
x
}
If I give it the following list, it behaves as expected:
> a = list(c(1,2,3), list(52, 561), "a")
> flatten(a)
[[1]]
[1] 1 2 3
[[2]]
[1] 52
[[3]]
[1] 561
[[4]]
[1] "a"
Now I'd like to restructure the flat list like a. relist fails miserably:
> relist(flatten(a), skeleton=a)
[[1]]
[[1]][[1]]
[1] 1 2 3
[[1]][[2]]
[1] 52
[[1]][[3]]
[1] 561
[[2]]
[[2]][[1]]
[[2]][[1]][[1]]
[1] "a"
[[2]][[2]]
[[2]][[2]][[1]]
NULL
[[3]]
[[3]][[1]]
NULL
Now, I could of course do relist(unlist(b), a) but that loses data types again. What is a good way to restructure a flat list?
Bonus points if it handles the analogous attribute to unlist.vectors correctly.
One way to do it is:
relist2 = function(x, like, relist.vectors=F) {
if (! relist.vectors)
like = rapply(a, function(f) NA, how='replace')
lapply(relist(x, skeleton=like), function(e) unlist(e, recursive=F))
}
This retains the classes and distinguishes between lists and vectors:
> relist2(flatten(a), like=a)
[[1]]
[1] 1 2 3
[[2]]
[[2]][[1]]
[1] 52
[[2]][[2]]
[1] 561
[[3]]
[1] "a"
> relist2(flatten(a, unlist.vectors=T), like=a, relist.vectors=T)
[[1]]
[1] 1 2 3
[[2]]
[[2]][[1]]
[1] 52
[[2]][[2]]
[1] 561
[[3]]
[1] "a"

adding a field to each element of a list

I have a list
> (mylist <- list(list(a=1),list(a=2),list(a=3)))
[[1]]
[[1]]$a
[1] 1
[[2]]
[[2]]$a
[1] 2
[[3]]
[[3]]$a
[1] 3
and I want to add field b to each sublist from 11:13 to get something like
> (mylist <- list(list(a=1,b=11),list(a=2,b=12),list(a=3,b=13)))
[[1]]
[[1]]$a
[1] 1
[[1]]$b
[1] 11
[[2]]
[[2]]$a
[1] 2
[[2]]$b
[1] 12
[[3]]
[[3]]$a
[1] 3
[[3]]$b
[1] 13
How do I do this?
(note that I have a large number of such relatively small lists, so this will be called in apply and has to be reasonably fast).
mylist <- list(list(a=1),list(a=2),list(a=3))
b.vals <- 11:13
mylist <- lapply(
1:length(mylist),
function(x) {
mylist[[x]]$b <- b.vals[[x]]
mylist[[x]]
} )

Ordering a list in arbitrary way

Is there a way to order a list in arbitrary way? For example, using a vector to order the list:
> a
[[1]]
[1] 1
[[2]]
[1] "B"
[[3]]
[1] 2
> b = c(3, 1, 2)
> magicfunction(a, b)
[[1]]
[1] 2
[[2]]
[1] 1
[[3]]
[1] "B"
Do you just mean re-ordering?
##Create some dummy data
R> a = list(3)
R> a[[1]] = 1; a[[2]] = "B"; a[[3]] = 2
R> b = c(3, 1, 2)
Then just order using a numeric vector:
R> a[b]
[[1]]
[1] 2
[[2]]
[1] 1
[[3]]
[1] "B"

Resources