Mapping, Filtering, and Reducing Collections
In the last chapter you learned to store things in lists, sets, and maps. Now we will learn to transform those collections without writing loops by hand. This style of working is common in Dart, and you will use it constantly in real projects.
Prerequisites: you should be comfortable with lists and functions from earlier chapters.
Three Everyday Moves
Most collection work boils down to three questions:
- Map: "Give me a new list where each item is changed somehow."
- Filter (called
wherein Dart): "Give me only the items that pass a test." - Reduce (called
foldin Dart): "Combine all items into one value, like a total."
Each of these takes a function — a small piece of code — and applies it to every item. That is why they are called higher-order functions: functions that accept other functions.
Mapping with map()
The map() method applies a function to each item and gives back the changed items.
void main() {
final prices = [3.0, 5.5, 2.25];
final withTax = prices.map((price) => price * 1.2);
print(withTax.toList()); // [3.6, 6.6, 2.7]
}The (price) => price * 1.2 part is a short function written in one line. It takes one price and returns the price plus tax. Note that map() is lazy — it does no work until you ask for the results, usually with .toList().
Filtering with where()
void main() {
final prices = [3.0, 15.5, 2.25];
final big = prices.where((price) => price > 5).toList();
print(big); // [15.5]
}The function returns true or false for each item. Only items that get true survive.
Chaining Moves Together
The real power comes from chaining. Here we take names, make them uppercase, then keep only the long ones:
void main() {
final names = ['Ada', 'Alan', 'Grace'];
final result = names
.map((n) => n.toUpperCase())
.where((n) => n.length > 3)
.toList();
print(result); // [ALAN, GRACE]
}Read it top to bottom like a recipe: map first, then filter, then collect into a list.
Reducing with fold()
fold() combines everything into a single value. You give it a starting value and a function that describes how to combine.
void main() {
final prices = [3.0, 5.5, 2.25];
final total = prices.fold(0.0, (sum, price) => sum + price);
print(total); // 10.75
}The starting value 0.0 becomes sum on the first pass; each price is added on.
caption: A chain of map, where, and fold transforming a list of prices.
alt: Flow diagram showing a list flowing through map to double values, then through where to keep one value, then through fold to produce a single total.
When to Use What
| You want... | Use |
|---|---|
| A changed copy of every item | `map()` |
| Only the items that pass a test | `where()` |
| One combined value | `fold()` |
A good rule of thumb: if you find yourself writing a loop that builds a new list, you can probably replace it with map() and where(). Both styles are valid; the chained style often shows the intent more clearly.
callout tip map() and where() are lazy. Nothing happens until you call .toList() or loop over the result. This is efficient for long chains because items flow through all steps one at a time.
exercise Q: Given the list [2, 4, 6, 8], write one chained expression that squares each number and keeps only results greater than 10. What is the result? A: [2, 4, 6, 8].map((n) => n * n).where((n) => n > 10).toList() gives [16, 36, 64].
exercise Q: What does ['a', 'bb', 'ccc'].fold(0, (len, s) => len + s.length) return? A: It returns 6 — the starting 0 plus 1, 2, and 3 characters.
estimate minutes: 25
Spotted a mistake, or code that does not work?