R - merge lists with rewriting and recursion

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 would like to recursively merge these lists by overwriting the entries if the second argument contains conflicting values. That is, the expected result will 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 clues?

+8
list r recursion
source share
2 answers

I think you will have to write your own recursive function here.

A function that takes on two lists, list1 and list2 . If a:

  • list1[[name]] exists, but not list2[[name]] , use list1[[name]] ;
  • list1[[name]] exists, as well as list2[[name]] , and both of them are not lists, use list2[[name]] ;
  • otherwise, recursion with list1[[name]] and list2[[name]] as 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 combined 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 combined list has a=2, b=3 . This is because the value from b$a overrides the value from a$a , although a$a is a list (you did not specify what would happen if that was the case). However, simply changing myMerge to handle such cases. Just remember - use is.list to check if it is a list, and is.null(myList$a) to see if there is an entry a in 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) } 
+9
source share

I'm not sure if a custom function is needed here. To perform this exact operation, there is a function utils::modifyList() ! See modifyList for details .

 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' 

What 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 
+6
source share

All Articles