Just wanted to share a bit of purrr learning about list_modify() and modify_in() and friends

I was confused at first because purrr::list_modify didn't do what I expected it to. Then I read the docs really carefully and realised why (when names match, the order doesn't get changed). Then I had a further look through the docs – I had a vague memory from using pluck that purrr had a few other functions for assigning into a list – and found the right function for the job!

So I thought I would share here - hope that's OK as it's not a question. (I should really get a blog).
My first time using any of these functions. Sorry for the slightly cumbersome example code.

library(purrr)

Create a list and an amendment to be applied to it:

a <- list(A = "Andrew", B = "Barbara", C = "Clive")
d <- list(D = "Deborah", E = "Edwin", F = "Flossie")
x <- list(a = a, d = d)

# let's say we want to change one item of the list:
d2 <- x[["d"]] %>% 
  `[`(c(3, 1:2))

This is a really basic way to give the desired output, but it is harder to put into a pipe sequence(?):

list(a = a, d = d2)
desired output
#> $a
#> $a$A
#> [1] "Andrew"
#> 
#> $a$B
#> [1] "Barbara"
#> 
#> $a$C
#> [1] "Clive"
#> 
#> 
#> $d
#> $d$F
#> [1] "Flossie"
#> 
#> $d$D
#> [1] "Deborah"
#> 
#> $d$E
#> [1] "Edwin"

Here's what I tried first. Output is unchanged because names are matched (original order kept):

x %>% 
  purrr::list_modify(d = d2)

#> $a
#> $a$A
#> [1] "Andrew"
#> 
#> $a$B
#> [1] "Barbara"
#> 
#> $a$C
#> [1] "Clive"
#> 
#> 
#> $d
#> $d$D
#> [1] "Deborah"
#> 
#> $d$E
#> [1] "Edwin"
#> 
#> $d$F
#> [1] "Flossie"

Ditto:

x %>% 
  purrr::update_list(d = d2)
same output, same reason

#> $a
#> $a$A
#> [1] "Andrew"
#> 
#> $a$B
#> [1] "Barbara"
#> 
#> $a$C
#> [1] "Clive"
#> 
#> 
#> $d
#> $d$D
#> [1] "Deborah"
#> 
#> $d$E
#> [1] "Edwin"
#> 
#> $d$F
#> [1] "Flossie"

These functions are what I needed :blush: :tada:, and are pipe-able… :heart_eyes:

x %>%
  purrr::assign_in("d", d2)
#> $a
#> $a$A
#> [1] "Andrew"
#> 
#> $a$B
#> [1] "Barbara"
#> 
#> $a$C
#> [1] "Clive"
#> 
#> 
#> $d
#> $d$F
#> [1] "Flossie"
#> 
#> $d$D
#> [1] "Deborah"
#> 
#> $d$E
#> [1] "Edwin"

… and this way is even neater imho, saving creating d2 in advance:

x %>% 
  purrr::modify_in("d", ~ `[`(., c(3, 1:2)))
#> $a
#> $a$A
#> [1] "Andrew"
#> 
#> $a$B
#> [1] "Barbara"
#> 
#> $a$C
#> [1] "Clive"
#> 
#> 
#> $d
#> $d$F
#> [1] "Flossie"
#> 
#> $d$D
#> [1] "Deborah"
#> 
#> $d$E
#> [1] "Edwin"

Created on 2021-07-30 by the reprex package (v2.0.0)

3 Likes

just replying so I can mark it closed.

Thanks for posting. I think self-answered questions are entirely appropriate and help build the community knowledge base. This is a good example of kindness to those who follow by unraveling a use case for the tidyverse approach.

Following is an alternative solution in {base}, not because it's better but because it shows how cognitive tradeoffs may be involved. I'll give my usual introduction to the approach I always start off with.

Every R problem can be thought of with advantage as the interaction of three objects— an existing object, x , a desired object,y , and a function, f, that will return a value of y given x as an argument. In other words, school algebra— f(x) = y. Any of the objects can be composites.

In this problem x is the list of lists called x and y is a list of lists derived from x that is identical except for the order of items in the second list of x. f is [, the subset operator to select the second list from x and to specify a new row order. My argument is that focusing on what, the desired output, rather than how reduces complexity. (I'm motivated because my working memory is not what it used to be.)

Thanks again. Hope the alternative is helpful.

library(magrittr)
a <- list(A = "Andrew", B = "Barbara", C = "Clive")
d <- list(D = "Deborah", E = "Edwin", F = "Flossie")
x <- list(a = a, d = d)

# let's say we want to change one item of the list:
d2 <- x[["d"]] %>%
  `[`(c(3, 1:2))

# However, since the desired output is to change x

x$d <- x$d[c(3,1:2)]

# result is the same
identical(d2,x$d)
#> [1] TRUE

# Full is is now
x
#> $a
#> $a$A
#> [1] "Andrew"
#> 
#> $a$B
#> [1] "Barbara"
#> 
#> $a$C
#> [1] "Clive"
#> 
#> 
#> $d
#> $d$F
#> [1] "Flossie"
#> 
#> $d$D
#> [1] "Deborah"
#> 
#> $d$E
#> [1] "Edwin"

# use in pipe

x <- list(a = a, d = d)

x$d[c(3,1:2)] %>% print()
#> $F
#> [1] "Flossie"
#> 
#> $D
#> [1] "Deborah"
#> 
#> $E
#> [1] "Edwin"

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.

If you have a query related to it or one of the replies, start a new topic and refer back with a link.