purrr iwalk() in R: Walk a List With Its Names
The iwalk() function in purrr applies a function to each element of a list or vector together with its name or index, purely for side effects like printing or saving files. It returns the original input invisibly, so it stays pipe-friendly.
iwalk(x, ~ cat(.y, .x, "\n")) # .x is value, .y is name/index iwalk(named_vec, ~ print(.y)) # .y is the element name iwalk(unnamed_vec, ~ print(.y)) # .y is the integer index iwalk(dfs, ~ write.csv(.x, .y)) # save each, name as path iwalk(x, function(val, nm) ...) # full anonymous function form iwalk(x, ~ message(.y)) # returns x invisibly
Need explanation? Read on for examples and pitfalls.
What iwalk() does in one sentence
iwalk() walks a list alongside its names. It calls your function once per element, passing the value as .x and the name as .y, and runs entirely for the side effect. The "i" stands for indexed: when the input has no names, .y becomes the integer position instead. It is the side-effect counterpart of imap(), which keeps return values.
x has no names, purrr substitutes seq_along(x). Knowing this equivalence makes every iwalk() call predictable: .x is always the value and .y is always the label.Syntax
iwalk() takes one input, one function, and nothing else. The signature is short because indexing is automatic.
Inside a formula-style function, .x is the current value and .y is its name or index. With a full anonymous function, the first argument receives the value and the second receives the name. The return value of .f is discarded; iwalk() always hands back .x invisibly.
Common use cases
iwalk() shines whenever the name carries meaning. Here are the patterns that come up most often.
Printing each element next to its label is the simplest case. The named vector supplies both halves of the message.
Splitting a data frame produces a named list, which iwalk() reports on cleanly. The split names (4, 6, 8) arrive as .y.
Saving each group to its own file is the headline use case. The name becomes part of the file path, so one call writes a tidy set of outputs.
When the input has no names, .y falls back to the position. This is handy for numbered logging or progress messages.
data |> iwalk(log_fn) |> map(transform_fn) to log every element and keep processing without breaking the chain.iwalk() vs walk2() vs imap()
iwalk() is the indexed, side-effect member of the walk family. The three functions differ on two axes: how many inputs they take and whether they return a usable value.
| Function | Inputs | .f receives |
Returns |
|---|---|---|---|
iwalk() |
one list/vector | value .x, name/index .y |
input, invisibly |
walk2() |
two parallel lists | element of each | first input, invisibly |
imap() |
one list/vector | value .x, name/index .y |
a new list |
Use iwalk() when one input already holds the labels you need. Use walk2() when the second value is genuinely separate data, not a name. Use imap() when you want the transformed result back instead of just a side effect.
Common pitfalls
iwalk() pitfalls cluster around the .x and .y order. Three mistakes cause most confusion.
Swapping the arguments is the classic error. The value is always .x and the name is always .y, even though "name first" feels natural when reading.
Expecting names from an unnamed input is the second trap. If .x has no names, .y is a position integer, not a label, so messages may read 1, 2, 3 unexpectedly.
imap(). Capturing out <- iwalk(...) gives you back the original input, not the computed values, which silently breaks code that expects transformed output.Try it yourself
Try it: Use iwalk() on the named vector caps to print each line as "<name> is the capital". The value .x is the country and .y is the city name.
Click to reveal solution
Explanation: .y holds the name (Paris, Tokyo) and .x holds the value (France, Japan). iwalk() runs cat() for its side effect and returns caps invisibly, so ex_out is the original vector.
Related purrr functions
iwalk() rarely works alone. These functions cover the rest of the iterate-with-index toolkit.
- imap(): the value-returning version of
iwalk(). - walk(): side effects over one input, no name needed.
- walk2(): side effects over two parallel lists.
- pwalk(): side effects over many parallel lists.
- map(): the foundational transform-and-return iterator.
FAQ
What is the difference between iwalk() and walk() in R?
walk() passes only the value to your function, while iwalk() passes both the value and its name or index. Choose iwalk() when the name matters, for example when the list name should become a file name or a label in printed output. Both run purely for side effects and both return their input invisibly, so the only practical difference is whether .f can see the name.
What does the .y argument mean in iwalk()?
In iwalk(), .y is the name of the current element. If the input vector or list has no names, .y becomes the integer index instead, starting at 1. The value itself is always .x. With a full anonymous function the same rule applies: the first argument is the value and the second is the name or index.
Does iwalk() return anything?
iwalk() returns its first argument .x invisibly. It is designed for side effects such as printing, plotting, or writing files, so the return value of your function is thrown away. If you assign the result, you get back the original input unchanged, which makes iwalk() safe to use in the middle of a pipe.
When should I use imap() instead of iwalk()?
Use imap() when you need the computed results, because it returns a new list of whatever .f produces. Use iwalk() when you only care about the side effect and want to keep the original data flowing through a pipe. The two share an identical calling convention, so switching between them is just a function-name change.
Can iwalk() loop over an unnamed list?
Yes. When the list or vector has no names, iwalk() uses seq_along() to supply integer indices as .y. This makes it a clean replacement for a for loop that needs a counter, without the manual index bookkeeping. The side effect runs once per element in order.
For the official argument reference, see the purrr iwalk documentation.