7

I often need some sort of counter / index value when applying loop-functions to vectors / list. When using basic loop functions, this index can be created by successively adding 1 to some initial value. Consider the following example:

lets <- letters[1:5]

n = 0
for (le in lets){
  n = n+1
  print(paste(le,"has index",n))
}
#> [1] "a has index 1"
#> [1] "b has index 2"
#> [1] "c has index 3"
#> [1] "d has index 4"
#> [1] "e has index 5"

The only way I've been able to access such an index value with the loop-functions from the purrr package is using map2. Is there a more elegant way to do this using only purrr::map()?

library(purrr)


map2(lets,1:length(lets),~paste(.x,"has index",.y))

#> [[1]]
#> [1] "a has index 1"
#> 
#> [[2]]
#> [1] "b has index 2"
#> 
#> [[3]]
#> [1] "c has index 3"
#> 
#> [[4]]
#> [1] "d has index 4"
#> 
#> [[5]]
#> [1] "e has index 5"
Ratnanil
  • 1,641
  • 17
  • 43

2 Answers2

5

The closest approximation of what you're looking for is purrr::imap, which is described in the documentation as

short hand for map2(x, names(x), ...) if x has names, or map2(x, seq_along(x), ...) if it does not.

The following code works:

lets <- letters[1:5]

purrr::imap(lets, ~print(paste(.x, "has index", .y)))

I'm assuming you are in fact trying to create a new object and store it in a new variable. If you are looking to show output (like in this example, where the result is a print to the console), you should use the equivalent function iwalk which returns its output invisibly.

A. Stam
  • 2,148
  • 14
  • 29
4

Try imap

lets <- letters[1:5]
purrr::imap(lets, ~paste(.x,"has index",.y))
#[[1]]
#[1] "a has index 1"

#[[2]]
#[1] "b has index 2"

#[[3]]
#[1] "c has index 3"

#[[4]]
#[1] "d has index 4"

#[[5]]
#[1] "e has index 5"

Note that imap will use the names of the elements of .x as .y argument if the elements are named. If you don't want that use imap(unname(...), ...) - thanks to @Moody_Mudskipper.


A base R approach could be

sprintf("%s has index %d", lets, seq_along(lets))
# [1] "a has index 1" "b has index 2" "c has index 3" "d has index 4" "e has index 5"
markus
  • 25,843
  • 5
  • 39
  • 58
  • 1
    note that `imap` will use the name of the element as `.y` if the elements are named, in that case `unname` first! – moodymudskipper Jan 14 '19 at 10:45
  • 2
    Thanks for this answer! @A. Stam posted [the same answer](https://stackoverflow.com/a/54179939/4139249) simultaneously. I've accepted his answer since it includes a reviling excerpt from the docs (_and_ he has less rep). – Ratnanil Jan 14 '19 at 11:05