Suppose I have two lists with names,
a = list( a=1, b=2, c=list( d=1, e=2 ), d=list( a=1, b=2 ) )
b = list( a=2, c=list( e=1, f=2 ), d=3, e=2 )
I'd like to recursively merge those lists, overwriting entries if the second argument contains conflicting values. I.e. the expected output would be
$a
[1] 2
$b
[1] 2
$c
$c$d
[1] 1
$c$e
[1] 1
$c$f
[1] 2
$d
[1] 3
$e
[1] 2
Any hint?
I am not so sure if a custom function is necessary here. There is a function utils::modifyList()
to perform this exact same operation! See modifyList for more info.
a <- list( a=1, b=2, c=list( d=1, e=2 ), d=list( a=1, b=2 ) )
b <- list( a=2, c=list( e=1, f=2 ), d=3, e=2 )
modifyList(a, b) # updates(modifies) 'a' with 'b'
Which gives the following
$a
[1] 2
$b
[1] 2
$c
$c$d
[1] 1
$c$e
[1] 1
$c$f
[1] 2
$d
[1] 3
$e
[1] 2
I think you'll have to write your own recursive function here.
A function that takes in two lists, list1
and list2
.
If:
list1[[name]]
exists but not list2[[name]]
, use list1[[name]]
;list1[[name]]
exists as well as list2[[name]]
and both are not lists, use list2[[name]]
;list1[[name]]
and list2[[name]]
as the new lists.Something like:
myMerge <- function (list1, list2) {
allNames <- unique(c(names(list1), names(list2)))
merged <- list1 # we will copy over/replace values from list2 as necessary
for (x in allNames) {
# convenience
a <- list1[[x]]
b <- list2[[x]]
if (is.null(a)) {
# only exists in list2, copy over
merged[[x]] <- b
} else if (is.list(a) && is.list(b)) {
# recurse
merged[[x]] <- myMerge(a, b)
} else if (!is.null(b)) {
# replace the list1 value with the list2 value (if it exists)
merged[[x]] <- b
}
}
return(merged)
}
Caveats - if your lists to be merged are weird, you might get weird output. For example:
a <- list( a=list(a=1, b=2), b=3 )
b <- list( a=2 )
Then your merged list has a=2, b=3
. This is because the value from b$a
overrides the value from a$a
, even though a$a
is a list (you did not specify what would happen if this were the case). However it is simple enough to modify myMerge
to handle these sorts of cases. Just remember - use is.list
to test if it's a list, and is.null(myList$a)
to see if entry a
exists in list myList
.
Here is the "vectorized" version using sapply
:
merge.lists <- function(a, b) {
a.names <- names(a)
b.names <- names(b)
m.names <- sort(unique(c(a.names, b.names)))
sapply(m.names, function(i) {
if (is.list(a[[i]]) & is.list(b[[i]])) merge.lists(a[[i]], b[[i]])
else if (i %in% b.names) b[[i]]
else a[[i]]
}, simplify = FALSE)
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With