For-each

Dear Computer

Chapter 4: Functions

For-each

Likely you've written a number of for loops to process an array or some other iterable collection. Your loops have looked something like one of these two pseudocode loops:

Pseudocode
for each item in items
  process item

for i in 0 to items.size
  process items[i]
for each item in items
  process item

for i in 0 to items.size
  process items[i]

Across the many situations where you have used these loops, the only part that changes is how each individual element is processed. Sometimes an item is printed. Sometimes its memory is freed. Sometimes it is reset. All of these different tasks may be subsumed by a single, higher-order for-each function that factors out the loop and leaves only the processing of the item as a parametric hole:

Pseudocode
class Collection
  items = ...

  function forEach(processOne)
    for i in 0 to items.size
      processOne(items[i], i)
class Collection
  items = ...

  function forEach(processOne)
    for i in 0 to items.size
      processOne(items[i], i)

Once this forEach function exists, you no longer need to write loops yourself. Instead you just defer to forEach, passing a processing function that serves as the loop body:

Pseudocode
items.forEach(print)
files.forEach(delete)
messages.forEach(send)
items.forEach(print)
files.forEach(delete)
messages.forEach(send)

Most Ruby collections support the for-each pattern through their each method. We call each on the collection and pass a block that processes a single element at time.

If an index is needed by the processing block, the each_with_index method is used instead. It passes the index as a second parameter to the block.

Ruby does have a for-each loop that can be used instead of a higher-order function:

Ruby
for tree in trees
  puts tree
end
for tree in trees
  puts tree
end

However, the Ruby community favors each and each_with_index. This runs counter to the Python and JavaScript communities, who tend to favor their builtin for-each loops. That's okay. The communities don't have to all be the same.

The for-each pattern is used to achieve side effects, like printing and mutating. The blocks don't return anything; they just change the state of the computer. If we do want to produce a new value, we use a different higher-order pattern, like map, filter, or fold.

← Higher-order FunctionsMap →