How to relocate several columns in one step using dplyr::relocate?

Viewed 3541

I would like to reorder some columns to come after a particular other column using dplyr::relocate. Here is a MWE:

a <- letters[1:3]
b <- letters[4:6]
c <- letters[7:9]
d <- letters[10:12]

mytib <- tibble::tibble(a,b,c,d)

#  A tibble: 3 x 4
#  a     b     c     d    
#  <chr> <chr> <chr> <chr>
# 1 a     d     g     j    
# 2 b     e     h     k    
# 3 c     f     i     l    

mytib %>%
     relocate(c, .after = a)

This example works but is there a way that I could, with one relocate command, move c after a and, say, d after b?

I tried the following without success:

mytib %>%
     relocate(c(c, d), .after(c(a, b)))

Edit 1: I explicitly ask about relocate because functions like select do not work for large datasets where all I know is after which column (name) I want to insert a column.

Edit 2: This is my expected output:

#  A tibble: 3 x 4
#  a     c     b     d    
#  <chr> <chr> <chr> <chr>
# 1 a     g     d     j    
# 2 b     h     e     k    
# 3 c     i     f     l   
2 Answers

As dplyr::relocate itself apparently doesn't allow relocating in pairs, you can "hack" this behavior by preparing a list of column pairs like the ones you describe ("c after a" & "d after b") and reduce over that list, passing your df in as an .init value and in each reduce-step relocating one pair.

Like this:

library(dplyr)
library(purrr)

df_relocated <- reduce(
  .x = list(c('c','a'), c('d','b')), 
  .f = ~ relocate(.x, .y[1], .after = .y[2]),
  .init = mytib
)

This produces a tibble just as you expect it:

> df_relocated
# A tibble: 3 x 4
  a     c     b     d    
  <chr> <chr> <chr> <chr>
1 a     g     d     j    
2 b     h     e     k    
3 c     i     f     l

In case you want to work with two lists, where element 1 of list 2 should relocated after element 1 of list 1 and so forth, this would be a solution:

 reduce2(
  .x = c("a", "b"),
  .y = c("c", "d"),
  .f = ~ relocate(..1, ..3, .after = ..2),
  .init = mytib
  )
Related