A logbook for curious waters
← Software Engineering Field Guide

Replace Loop with Pipeline

Before you begin: Loops, collections, predicates, and simple lambda expressions.

Replace Loop with Pipeline expresses a collection transformation as stages such as filtering, mapping, and combining. Here a pipeline means that the output of one stage becomes the input of the next. It does not imply threads, network services, or asynchronous execution. 1

Original version: collect doubled positive rewards

A reward list can contain zero and negative adjustments. This operation keeps only positive values and doubles them, preserving their order. Assume small integers without overflow.

public static class Rewards
{
    public static int[] Bonuses(int[] amounts)
    {
        var result = new List<int>();
        foreach (int amount in amounts)
        {
            if (amount > 0) result.Add(amount * 2);
        }
        return result.ToArray();
    }
}
def bonuses(amounts):
    result = []
    for amount in amounts:
        if amount > 0:
            result.append(amount * 2)
    return result

The loop handles selection, transformation, and storage. It is already reasonable code; the alternative names the first two jobs more directly.

Refactored version: filter, transform, collect

public static class Rewards
{
    public static int[] Bonuses(int[] amounts)
    {
        return amounts
            .Where(amount => amount > 0)
            .Select(amount => amount * 2)
            .ToArray();
    }
}

Where keeps positive values. Select doubles each retained value. ToArray executes the deferred query and returns a completed array, as the original method did. Omitting that final collection step would change the result type and when the work happens. 2

def bonuses(amounts):
    return [amount * 2 for amount in amounts if amount > 0]

The list comprehension expresses filtering and transformation in one collection expression. It returns a completed list, not a generator. The if selects an amount before the leading expression doubles it.

For -1, 3, 0, 2, both versions return 6, 4. Nothing sorts the result or processes it in parallel.

Prefer a loop when the loop tells the story better

A transformation with several early exits, complex state, or important side effects may be clearer as an ordinary loop. A long chain of unfamiliar functions can be harder to read than the code it replaces.

Preserve order, number of evaluations, exception timing, and whether the result is eager or lazy. A pipeline over a database query may translate into SQL rather than execute like an in-memory loop. The example’s arrays and lists avoid that additional distinction.

Use the transformation vocabulary when it makes the operation recognizable: filter these values, map them to those values, then collect. Do not substitute compact syntax merely to make the original code look old-fashioned.

Sources

  1. Martin Fowler — Replace Loop with Pipeline.
  2. Microsoft — Deferred execution of LINQ queries.
  3. Python — List comprehensions.