I have two lists, whose elements have partially overlapping names, which I need to merge/combine together into a single list, element by element:
My question is rela
Here you go in 3 lines:
out <- l.1
mnames <- intersect(names(l.1),names(l.2))
out[mnames] <- Map(function(a,b) Map(c,a,b),l.1[mnames],l.2[mnames])
#$a
#$a[[1]]
#[1] 10 20
#$a[[2]]
#[1] 1 0
#
#$b
#$b[[1]]
#[1] 10 20 30
#$b[[2]]
#[1] 1 2 3
#
#$c
#$c[[1]]
#[1] 9 12 13
#$c[[2]]
#NULL
Here is an additional solution. It uses mapply
with c
to combine the lists:
## get all possible names
l.names <- union(names(l.1), names(l.2))
## combine lists
r <- mapply(c, l.1[l.names], l.2[l.names])
## get rid of NULL entries
l.3 <- sapply(names(r),
function(x) r[[x]][!sapply(r[[x]], is.null)], USE.NAMES=TRUE)
I adapted this answer from answers found on this SO question on merging two lists and this R help question on how to delete null elements in a list.
The first line gathers the names present in at least one of the two lists (i.e. all possible names). The second line uses mapply
, c
, and list indexing with the previously gathered names to combine the lists, albeit with extra NULL
entries present. The third line gets rid of these NULL
entries while preserving list names.
Note this answer does get rid of the NULL
entry for list element c
.
You can use lapply
operating on the keys to do this merge:
keys <- unique(c(names(l.1), names(l.2)))
setNames(lapply(keys, function(key) list(c(l.1[[key]][[1]], l.2[[key]][[1]]),
c(l.1[[key]][[2]], l.2[[key]][[2]]))),
keys)
# $a
# $a[[1]]
# [1] 10 20
#
# $a[[2]]
# [1] 1 0
#
# $b
# $b[[1]]
# [1] 10 20 30
#
# $b[[2]]
# [1] 1 2 3
#
# $c
# $c[[1]]
# [1] 9 12 13
#
# $c[[2]]
# NULL
Inspired by josilber's answer, here we do not hard-code the length of the sublists and use lapply
to create them in the result:
keys <- unique(c(names(l.1), names(l.2)))
setNames(lapply(keys, function(key) {
l1 <- l.1[[key]]
l2 <- l.2[[key]]
len <- max(length(l1), length(l2))
lapply(seq(len), function(i) c(l1[[i]], l2[[i]]))
}),
keys)
This is a kind of a nested merge function which seems to produce the output you desire. I feel like there should be a more simple way but I can't think of one. It will prefer values from the first parameter, but will merge with values from the second parameter if there is a matching name or index.
nestedMerge<-function(a,b) {
if(is.list(a) & is.list(b)) {
out<-list()
if(!is.null(names(a))) {
for(n in names(a)) {
if(n %in% names(b) && !is.null(b[[n]])) {
out<-append(out, list(Recall(a[[n]], b[[n]])))
} else {
out<-append(out, list(a[[n]]))
}
names(out)[length(out)]<-n
}
} else {
for(i in seq_along(a))
if(i <=length(b) && !is.null(b[[i]])) {
out<-append(out, Recall(a[[i]], b[[i]]))
} else {
out<-append(out, list(a[[i]]))
}
}
return(out)
} else {
return(list(c(a,b)))
}
}
#and now, use the function
nestedMerge(l.1,l.2)