8

I have a list like:

mylist <- list(a = 1, b = list(A = 1, B = 2), c = list(C = 1, D = 3))

is there an (loop-free) way to identify the positions of the elements, e.g. if I want to replace a values of "C" with 5, and it does not matter where the element "C" is found, can I do something like:

Aindex <- find_index("A", mylist)
mylist[Aindex] <- 5

I have tried grepl, and in the current example, the following will work:

mylist[grepl("C", mylist)][[1]][["C"]]

but this requires an assumption of the nesting level.

The reason that I ask is that I have a deep list of parameter values, and a named vector of replacement values, and I want to do something like

 replacements <- c(a = 1, C = 5)
 for(i in names(replacements)){ 
    indx <- find_index(i, mylist)
    mylist[indx] <-  replacements[i]
  }

this is an adaptation to my previous question, update a node (of unknown depth) using xpath in R?, using R lists instead of XML

Community
  • 1
  • 1
Abe
  • 12,956
  • 12
  • 51
  • 72

3 Answers3

8

One method is to use unlist and relist.

mylist <- list(a = 1, b = list(A = 1, B = 2), c = list(C = 1, D = 3))
tmp <- as.relistable(mylist)
tmp <- unlist(tmp)
tmp[grep("(^|.)C$",names(tmp))] <- 5
tmp <- relist(tmp)

Because list names from unlist are concatenated with a ., you'll need to be careful with grep and how your parameters are named. If there is not a . in any of your list names, this should be fine. Otherwise, names like list(.C = 1) will fall into the pattern and be replaced.

Blue Magister
  • 13,044
  • 5
  • 38
  • 56
1

Based on this question, you could try it recursively like this:

find_and_replace <- function(x, find, replace){
  if(is.list(x)){
    n <- names(x) == find
    x[n] <- replace
    lapply(x, find_and_replace, find=find, replace=replace)
  }else{
    x
  }
}

Testing in a deeper mylist:

mylist <- list(a = 1, b = list(A = 1, B = 2), c = list(C = 1, D = 3, d = list(C=10, D=55)))
find_and_replace(mylist, "C", 5)
$a
[1] 1

$b
$b$A
[1] 1

$b$B
[1] 2


$c
$c$C  ### it worked
[1] 5

$c$D
[1] 3

$c$d
$c$d$C ### it worked
[1] 5

$c$d$D
[1] 55
Community
  • 1
  • 1
Carlos Cinelli
  • 11,354
  • 9
  • 43
  • 66
0

This can now also be done using rrapply in the rrapply-package (an extended version of base rapply). To return the position of an element in the nested list based on its name, we can use the special arguments .xpos and .xname. For instance, to look up the position of the element with name "C":

library(rrapply)

mylist <- list(a = 1, b = list(A = 1, B = 2), c = list(C = 1, D = 3))

## get position C-node
(Cindex <- rrapply(mylist, condition = function(x, .xname) .xname == "C", f = function(x, .xpos) .xpos, how = "unlist"))
#> c.C1 c.C2 
#>    3    1

We could then update its value in the nested list with:

## update value C-node
mylist[[Cindex]] <- 5

The two steps can also be combined directly in the call to rrapply:

rrapply(mylist, condition = function(x, .xname) .xname == "C", f = function(x) 5, how = "replace")
#> $a
#> [1] 1
#> 
#> $b
#> $b$A
#> [1] 1
#> 
#> $b$B
#> [1] 2
#> 
#> 
#> $c
#> $c$C
#> [1] 5
#> 
#> $c$D
#> [1] 3
Joris C.
  • 5,721
  • 3
  • 12
  • 27