2012-12-11 15 views
8

Angenommen, ich habe zwei Listen mit Namen,R - fusionieren Listen mit überschreiben und Rekursion

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) 

Ich mag würde rekursiv diese Listen zusammenführen und überschreibt Einträge, wenn das zweite Argument widersprüchliche Werte enthält. I.e. die erwartete Ausgabe wäre

$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 

Irgendwelche Hinweise?

Antwort

9

Ich denke, dass Sie hier Ihre eigene rekursive Funktion schreiben müssen.

Eine Funktion, die zwei Listen akzeptiert, list1 und list2. If:

  • list1[[name]] existiert, aber nicht list2[[name]], verwenden list1[[name]];
  • list1[[name]] existiert ebenso wie list2[[name]] und beide sind keine Listen, verwenden Sie list2[[name]];
  • andernfalls mit list1[[name]] und list2[[name]] als die neuen Listen rekursiv.

Etwas wie:

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 - wenn Ihre Listen sind seltsam zusammengeführt werden, könnten Sie seltsame Ausgabe erhalten. Zum Beispiel:

a <- list(a=list(a=1, b=2), b=3) 
b <- list(a=2) 

dann Ihre fusionierte Liste a=2, b=3. Dies liegt daran, dass der Wert b$a den Wert von a$a überschreibt, auch wenn a$a eine Liste ist (Sie haben nicht angegeben, was passieren würde, wenn dies der Fall wäre). Es ist jedoch einfach genug, myMerge zu modifizieren, um diese Art von Fällen zu behandeln. Erinnern Sie sich einfach - benutzen Sie is.list, um zu testen, ob es eine Liste ist, und is.null(myList$a), um zu sehen, ob der Eintrag a in der Liste myList existiert.


Hier ist die "vektorisiert" Version sapply mit:

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) 
} 
+0

gibt Ich bin nicht sicher, ob diese vektorisiert werden kann mit 'vapply'/ähnlich, während immer noch die verschachtelte Struktur zu halten. –

+0

Das ist großartig! Genau das, was ich brauche. BTW, mein Beispiel hatte den "seltsamen" Listeneintrag ("d" in beiden Listen). – zeeMonkeez

+0

Wenn Sie 'if (! Is.null (a) &&! Is.null (b) &&xor (is.list (a), is.list (b))) { fusioniert [[x]] <- c (a, b) } ' am Anfang dieser if-Anweisung können Sie diesen Kantenfall berücksichtigen, wobei das Überschreiben freilich verloren geht. –

6

Ich bin nicht so sicher, ob eine benutzerdefinierte Funktion hier notwendig ist. Es gibt eine Funktion utils::modifyList(), um genau diesen Vorgang auszuführen! Weitere Informationen finden Sie unter modifyList.

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' 

, das die folgenden

$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 
+0

Ich bin mir nicht sicher, warum dies nicht die richtige Antwort ist – rmg