Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Assign values with same name in a nested list in R

Tags:

r

nested-lists

I want to assign the same value to specific elements in nested lists that have the same name. I would also like to create the element if it didn't exist in the nested list, but this isn't shown in my example.

For example, let's say I have:

ls <- list(a = list(e1 = "value1.1", e2 = "value1.2"),
           b = list(e1 = "value2.1", e2 = "value2.2"))

And I want to assign the value "same value" to all the elements in sublists that are named e1 so that I would end up with this desired output:

list(a = list(e1 = "same value", e2 = "value1.2"),
     b = list(e1 = "same value", e2 = "value2.2")
> ls
  $a
  $a$e1
  [1] "same value"

  $a$e2
  [1] "value1.2"


  $b
  $b$e1
  [1] "same value"

  $b$e2
  [1] "value2.2"

After researches, I found the function modify_depth() in the package purrr that will apply a function to the nested lists, but won't assign a value.

My only other solution was to do this:

ls2 <- list()
for(sublist in ls){
  sublist[["e1"]] <- "same value"
  ls2 <- c(ls2, list(sublist))
}
names(ls2) <- names(ls)
ls <- ls2
rm(ls2)

Note: after the loop, "same value" are not assigned in ls, so I have to create ls2. I could make this a function, but I'm sure there is a better way to do this, without a for loop.

Thanks for your help!

like image 878
A Parent Avatar asked Oct 17 '22 13:10

A Parent


2 Answers

With map and replace from purrr:

library(purrr)
map(ls, ~replace(., "e1", "same value"))

or with modify_depth:

modify_depth(ls, 1, ~replace(., "e1", "same value"))

replace also works with lapply:

lapply(ls, replace, "e1", "same value")

Output:

$a
$a$e1
[1] "same value"

$a$e2
[1] "value1.2"


$b
$b$e1
[1] "same value"

$b$e2
[1] "value2.2"

The good thing about modify_depth is that you can choose the depth, whereas map and lapply only goes down one level:

ls2 <- list(c = list(a1 = list(e1 = "value1.1", e2 = "value1.2")),
            d = list(a1 = list(e1 = "value2.1", e2 = "value2.2")))

modify_depth(ls2, 2, ~replace(., "e1", "same value"))

Output:

$c
$c$a1
$c$a1$e1
[1] "same value"

$c$a1$e2
[1] "value1.2"


$d
$d$a1
$d$a1$e1
[1] "same value"

$d$a1$e2
[1] "value2.2"

Edit: If we want to apply a vector of values to each e1 element of ls2, modify_depth would not work since it does not have a map2 variant. Instead, I would use two layers of map's, map2 on the top layer, and map on the second layer.

vec <- c("newvalue1.1", "newvalue2.1")

map2(ls2, vec, ~map(.x, replace, "e1", .y))

Output:

$c
$c$a1
$c$a1$e1
[1] "newvalue1.1"

$c$a1$e2
[1] "value1.2"


$d
$d$a1
$d$a1$e1
[1] "newvalue2.1"

$d$a1$e2
[1] "value2.2"
like image 182
acylam Avatar answered Oct 21 '22 01:10

acylam


We can use lapply to iterate over list elements, then using [ with logical operator for equality == we can locate the values we want to change

> lapply(ls, function(x){
    x[names(x)=="e1"] <- "same.value"
    x
  } )
$`a`
$`a`$`e1`
[1] "same.value"

$`a`$e2
[1] "value1.2"


$b
$b$`e1`
[1] "same.value"

$b$e2
[1] "value2.2"
like image 34
Jilber Urbina Avatar answered Oct 21 '22 02:10

Jilber Urbina