Iterator / For-Each
A tool that processes items in a list or collection one by one, allowing you to perform actions on each item without manually managing the details of how to access them.
What Is an Iterator / For-Each?
An iterator is an object or logic block that enables traversal through a collection, processing each item sequentially. Iterators provide a standardized way to access elements in lists, arrays, maps, and other data structures without directly managing indices or internal implementation details. This abstraction makes code more readable, maintainable, and less prone to common errors like off-by-one mistakes.
A for-each construct executes a block of code for every item in a collection, abstracting away iteration mechanics. Most modern programming languages provide for-each as a built-in statement—JavaScript’s for...of, C#’s foreach, and Java’s enhanced for loop. These constructs are also fundamental in workflow automation platforms, where visual iterator blocks process lists without requiring code.
Iterators and for-each loops form the backbone of collection processing in software development and automation. They simplify repetitive tasks, reduce errors, and make code intention clearer. Whether processing spreadsheet rows, handling API responses, or automating workflow tasks, these constructs are essential tools for developers and automation specialists.
Core Concepts
Iterator Protocol
An iterator must provide a mechanism to return the next item and signal when no more items remain. This is formalized as an iterator protocol across languages:
Key Requirements:
- Next Method: Returns the next item in sequence
- End Detection: Signals when traversal is complete (StopIteration, done flag, hasNext returning false)
- Consumption: Most iterators are consumed during traversal and cannot be reset without creating a new instance
Iterable vs Iterator:
- Iterable: Object that can produce an iterator (lists, arrays, sets)
- Iterator: Object that delivers items one at a time from an iterable
In Python, every iterator is also an iterable, but not every iterable is an iterator. Lists are iterable but not iterators—calling iter() on a list produces an iterator.
For-Each Advantages
Error Reduction
Eliminates off-by-one errors, index management mistakes, and accidental element skipping that plague traditional for loops.
Code Clarity
Shorter, more readable code that communicates intent directly. Compare for item in items versus managing counters and bounds.
Safety
Works with any iterable/collection object, reducing coupling to underlying data structure implementation.
Maintainability
Changes to collection type rarely require changes to iteration code when using for-each.
Language-Specific Implementations
Python
Python formalizes iteration through the iterator protocol. Objects implement __iter__() (returns iterator) and __next__() (returns next item or raises StopIteration).
Basic Usage:
my_list = [10, 20, 30]
my_iter = iter(my_list)
print(next(my_iter)) # 10
print(next(my_iter)) # 20
print(next(my_iter)) # 30
# next(my_iter) would raise StopIteration
For Loop (Recommended):
for item in my_list:
process(item)
Custom Iterator:
class Counter:
def __iter__(self):
self.count = 0
return self
def __next__(self):
if self.count < 5:
self.count += 1
return self.count
raise StopIteration
for num in Counter():
print(num) # 1 2 3 4 5
Best Practices:
- Use for loops for most iteration tasks
- Avoid modifying collections during iteration
- Use list comprehensions for transformations:
[x*2 for x in items]
JavaScript
JavaScript implements the iterator protocol through objects with a next() method returning {value, done}. Built-in types (Array, String, Set, Map) are iterable via their [Symbol.iterator]() method.
Manual Iteration:
const arr = [1, 2, 3];
const iter = arr[Symbol.iterator]();
console.log(iter.next()); // {value: 1, done: false}
console.log(iter.next()); // {value: 2, done: false}
console.log(iter.next()); // {value: 3, done: false}
console.log(iter.next()); // {value: undefined, done: true}
For-of Loop (Recommended):
for (const item of arr) {
console.log(item);
}
Custom Iterator:
function makeRangeIterator(start = 0, end = 5) {
let current = start;
return {
next: () => {
if (current < end) {
return {value: current++, done: false};
}
return {done: true};
}
};
}
Generator Functions:
function* genNumbers() {
yield 1;
yield 2;
yield 3;
}
for (const num of genNumbers()) {
console.log(num);
}
Best Practices:
- Use
for...offor arrays and iterables - Remember iterators are consumed after one pass
- Use generators for complex iteration logic
Java
Java provides the Iterator<E> interface with three methods: hasNext(), next(), and remove() (optional). Collections framework classes implement this interface for traversal.
Iterator Usage:
ArrayList<String> cars = new ArrayList<>();
cars.add("Volvo");
cars.add("BMW");
Iterator<String> it = cars.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
For-each Loop (Preferred):
for (String car : cars) {
System.out.println(car);
}
Safe Element Removal:
Iterator<Integer> it = numbers.iterator();
while (it.hasNext()) {
if (it.next() < 10) {
it.remove(); // Safe removal during iteration
}
}
Best Practices:
- Use for-each for read-only iteration
- Use Iterator directly only when removing elements
- Never modify collections directly during iteration (throws ConcurrentModificationException)
C#
C# uses the IEnumerator interface with MoveNext(), Current, and Reset() methods. The foreach statement provides convenient iteration over any IEnumerable or IEnumerable<T> type.
Foreach Statement:
List<string> colors = new List<string> {"Red", "Green", "Blue"};
foreach (var color in colors)
{
Console.WriteLine(color);
}
Manual Enumeration:
var enumerator = colors.GetEnumerator();
while (enumerator.MoveNext())
{
Console.WriteLine(enumerator.Current);
}
Custom Iterator with yield:
IEnumerable<int> GetNumbers()
{
for (int i = 0; i < 3; i++)
yield return i;
}
foreach (var n in GetNumbers())
{
Console.WriteLine(n);
}
Asynchronous Iteration:
await foreach (var item in asyncSequence)
{
// Process async data stream
}
Best Practices:
- Use
foreachfor readability and safety - Direct modification during
foreachis not allowed - Use
yield returnfor custom sequences
Workflow Automation (Relay.app)
Workflow automation platforms provide visual iterator blocks for processing lists without code. These blocks handle common automation tasks like processing spreadsheet rows, email attachments, or API response arrays.
Setup Process:
- Add iterator block from Flow Control menu
- Select list to process (from previous step output)
- Configure actions to perform on each item
- Reference current item data using block variables
Common Use Cases:
- Process each spreadsheet row
- Send individual notifications
- Update records one by one
- Transform data items
Best Practices:
- Place all per-item actions inside iterator block
- Avoid modifying source list during iteration
- Use iterator output for downstream steps
- Handle errors gracefully with fallback actions
Comparison Table
| Concept | Description | When to Use |
|---|---|---|
| Iterator | Object producing items one by one | Fine control over iteration |
| Iterable | Object that can return an iterator | Loop with for-each |
| For Loop | Classic loop with counters | Need index or custom steps |
| For-Each | Simplified loop hiding indices | Just process items |
Key Differences:
- For-each doesn’t expose indices directly
- For-each safer for read-only operations
- For loops needed for custom step sizes, skipping, reverse order
- Some languages allow item removal during iteration (Java), others don’t (C#)
Common Patterns and Best Practices
Processing Collections
Data Transformation:
# Python list comprehension
squared = [x**2 for x in numbers]
# JavaScript map
const squared = numbers.map(x => x**2);
Filtering:
# Python filter with comprehension
evens = [x for x in numbers if x % 2 == 0]
# JavaScript filter
const evens = numbers.filter(x => x % 2 === 0);
Aggregation:
# Python reduce
from functools import reduce
total = reduce(lambda acc, x: acc + x, numbers, 0)
# JavaScript reduce
const total = numbers.reduce((acc, x) => acc + x, 0);
Avoiding Common Pitfalls
Don’t Modify During Iteration:
# Wrong
for item in items:
if condition(item):
items.remove(item) # Causes issues
# Right
items = [item for item in items if not condition(item)]
Don’t Reuse Consumed Iterators:
const iter = arr[Symbol.iterator]();
for (const x of iter) { /* first pass */ }
for (const x of iter) { /* won't work - iterator consumed */ }
Handle Index Requirements:
# When you need indices
for i, item in enumerate(items):
print(f"Item {i}: {item}")
Special Features
Asynchronous Iteration:
- C#:
await foreachfor async streams - JavaScript:
for await...offor async iterables
Removing Elements:
- Java:
Iterator.remove()during iteration - Most others: Create new filtered collection
Infinite Sequences:
def infinite_counter():
n = 0
while True:
yield n
n += 1
Practical Use Cases
Spreadsheet Processing
Python with CSV:
import csv
with open('data.csv') as f:
for row in csv.DictReader(f):
process_row(row)
JavaScript with Arrays:
for (const row of spreadsheetData) {
validateAndSave(row);
}
API Response Handling
Processing JSON Arrays:
const response = await fetch(apiUrl);
const data = await response.json();
for (const user of data.users) {
await updateUserProfile(user);
}
Batch Operations
Processing Files:
import os
for filename in os.listdir('input/'):
if filename.endswith('.txt'):
process_file(f'input/{filename}')
Database Updates:
foreach (var record in records)
{
record.UpdatedAt = DateTime.Now;
db.SaveChanges();
}
Workflow Automation
Email List Processing:
- Iterator receives email list from trigger
- Each iteration sends personalized message
- Logs results for each recipient
Data Enrichment:
- Iterator processes customer records
- Each iteration calls enrichment API
- Saves enhanced data to database
Performance Considerations
Memory Efficiency:
- Iterators process items on-demand (lazy evaluation)
- Generators and yield statements minimize memory usage
- Avoid materializing entire collections when possible
Optimization Tips:
- Use built-in iteration methods (map, filter) when available
- Consider parallel processing for independent operations
- Batch database operations when feasible
Benchmarking:
# Python timeit for performance testing
import timeit
# Compare approaches
time_for = timeit.timeit(lambda: [x*2 for x in range(1000)])
time_map = timeit.timeit(lambda: list(map(lambda x: x*2, range(1000))))
References
Related Terms
Aggregator
A node that collects outputs from multiple execution paths or loops and combines them into a single ...
Automated Workflows
Automated workflows are digital processes that handle repetitive business tasks automatically based ...
Automation Platform
An automation platform is software that handles repetitive business tasks automatically by connectin...
Automation Rules
Automation rules are settings that automatically perform tasks when specific events happen and condi...
Make (Integromat)
A no-code platform that automates repetitive tasks by visually connecting apps and services without ...