Combine lists while overriding values with same name in R - r

I have two lists with named elements:
a <- list(a=1, b=2)
b <- list(b=3, c=4)
I want to combine these lists, so that any elements in a that have the same names will be overwritten by the list b, so I get this out:
list(a=1, b=3, c=4)
I know I could do this in a loop, but is there a more compact way of doing this in R?

R has a built in function to do that modifyList
modifyList(a, b)

Here's a simple solution:
# create new list
newlist <- c(a,b)
# remove list element(s)
newlist[!duplicated(names(newlist), fromLast = TRUE)]
The result:
$a
[1] 1
$b
[1] 3
$c
[1] 4
An even simpler solution with setdiff:
c(a[setdiff(names(a), names(b))], b)
$a
[1] 1
$b
[1] 3
$c
[1] 4

Related

Merging two lists with similar elements in R [duplicate]

I have two lists with named elements:
a <- list(a=1, b=2)
b <- list(b=3, c=4)
I want to combine these lists, so that any elements in a that have the same names will be overwritten by the list b, so I get this out:
list(a=1, b=3, c=4)
I know I could do this in a loop, but is there a more compact way of doing this in R?
R has a built in function to do that modifyList
modifyList(a, b)
Here's a simple solution:
# create new list
newlist <- c(a,b)
# remove list element(s)
newlist[!duplicated(names(newlist), fromLast = TRUE)]
The result:
$a
[1] 1
$b
[1] 3
$c
[1] 4
An even simpler solution with setdiff:
c(a[setdiff(names(a), names(b))], b)
$a
[1] 1
$b
[1] 3
$c
[1] 4

Fail to join the variables with its name into a list using lapply?

I have tried to use the code below but failed. I want to know why it failed and what's the correct (and elegant) way to do that?
a <- 1
b <- 2
res <- lapply(ls(), function(x, l) { l$x <- get(x)}, l=list())
I hope I get the result like
res
# $a
# [1] 1
# $b
# [1] 2
but what I get is
res
# [[1]]
# [1] 1
# [[2]]
# [1] 2
We can use mget to obtain the value of more than one object and it returns a named list
mget(ls())
#$a
#[1] 1
#$b
#[1] 2
If we need to use get, then set the names with ls()
setNames(lapply(ls(), get), ls())
Using sapply:
sapply(ls(), get, simplify = FALSE)
# $a
# [1] 1
#
# $b
# [1] 2
sapply has simplify and USE.NAMES arguments, both have default values of TRUE. So by setting simplify to FALSE we are keeping the result as named list.

Subset different vector elements within a list

Assume I have this list of vectors:
mylist <- list(a=1:3,b=4:1,c=1:5)
mylist
$a
[1] 1 2 3
$b
[1] 4 3 2 1
$c
[1] 1 2 3 4 5
I want to get the last or the max element of each vector like this for the last element:
$a
[1] 3
$b
[1] 1
$c
[1] 5
What I have tried so far:
First use lapply and the length function to get the last element index and then subset:
last <- unlist(lapply(mylist, length))
lapply(mylist,"[", last) # not working
Then I tried to use sapply with lapply. This is working, but I'm not sure whether this is generally valid. There must be a better base R solution (without loops!).
mymatrix <- sapply(last, function(x) lapply(mylist, "[",x))
diag(mymatrix)
$a
[1] 3
$b
[1] 1
$c
[1] 5
(Making this a CV as there were many contributes here and worth summing this up)
If you have some function you want to apply on your list, a simple lapply should do, such as
lapply(mylist, max) # retrieving the maximum values
Or
lapply(mylist, tail, 1) # retrieving the last values (by #docendo)
If you want to operate on two vectors simultaneously, you could use mapply or Map
Map(`[`, mylist, lengths(mylist)) # A Map version of #docendos lapply suggestion
Or per your newest request
Map(`[`, mylist, 1:3)

How to get a named list element in R if the appearance of the element is conditional?

I want to include a list element c in a list L in R and name it C.
The example is as follows:
a=c(1,2,3)
b=c("a","b","c")
c=rnorm(3)
L<-list(A=a,
B=b,
C=c)
print(L)
## $A
## [1] 1 2 3
##
## $B
## [1] "a" "b" "c"
##
## $C
## [1] -2.2398424 0.9561929 -0.6172520
Now I want to introduce a condition on C, so it is only included in
the list if C.bool==T:
C.bool<-T
L<-list(A=a,
B=b,
if(C.bool) C=c)
print(L)
## $A
## [1] 1 2 3
##
## $B
## [1] "a" "b" "c"
##
## [[3]]
## [1] -2.2398424 0.9561929 -0.6172520
Now, however, the list element of c is not being named as specified in
the list statement. What's the trick here?
Edit: The intention is to only include the element in the list if the condition is met (no NULL shoul be included otherwise). Can this be done within the core definition of the list?
I don't know why you want to do it "without adding C outside the core definition of the list?" but if you're content with two lists in a single c then:
L <- c(list(A=a, B=b), if(C.bool) list(C=c))
If you really want one list but don't mind subsetting after creation then
L <- list(A=a, B=b, C=if(C.bool) c)[c(TRUE, TRUE, C.bool)]
(pace David Arenburg, isTRUE() omitted for brevity)
you can try this if you want to keep the names
L2 <-list(A=a,
B=b,
C = if (TRUE) c)
You can of course replace TRUE with the statement containing C.bool
You could place the if statement outside the core definition of the list, like this:
L <- list(A = a, B= b)
if (isTRUE(C.bool)) L$C <- c
#> L
#$A
#[1] 1 2 3
#
#$B
#[1] "a" "b" "c"
#
#$C
#[1] -0.7631459 0.7353929 -0.2085646
(Edit with isTRUE() owing to the comment by #DavidArenburg)
As a combination of the previous answers by #MamounBenghezal, #user20637
and the comment made by #DavidArenburg, I would suggest this generalized
version that does not depend on the length of the list:
L <- Filter(Negate(is.null),
x = list(A = a, B = b, C = if (isTRUE(C.bool)) c, D = "foo"))

combine lists in R without knowing beforehand the number of lists to combine

If I was starting with a list A that contained lists N and M, which contain equal number of elements:
A = list( N=list( a=c(1,1), b=c(2,2)),
M=list( a=c(1,1), b=c(2,2)) )
I would do the following to combine each of the elements from the lists N and M into new lists
B = mapply( FUN=list, A[[1]], A[[2]], SIMPLIFY=FALSE )
to get
>B
$a
$a[[1]]
[1] 1 1
$a[[2]]
[1] 1 1
$b
$b[[1]]
[1] 2 2
$b[[2]]
[1] 2 2
How can I do the same thing as above if I don't know beforehand the number of lists that the list A will have?
There is probably a better solution to your problem, but if you literally want to "do the same thing, but without knowing size of A", you can do the following:
do.call(function(...) mapply(..., FUN = list, SIMPLIFY = FALSE), A)
try using tapply:
tapply(U, inds, list)
Where
U <- unlist(A, recursive=FALSE)
inds <- rep(seq_along(A[[1]]), length(A))

Resources