Skip to main content

Menu

System onlineAI Tutorials / 2026

AI Tutorials
Knowledge search

Recent · Popular · Tutorials

  • dart3 tutorials
  • java1 tutorial
  • Javascript3 tutorials
  • Node.js Introduction2 tutorials
  • dartdart · General · In depth
  • JavascriptJavascript · Advanced · In depth
  • JavascriptJavascript · Advanced · Mid
  • JavascriptJavascript · Advanced · Short

Find a tutorial

Tell the assistant what you want to learn. It suggests published chapters.

For example: “I want to learn Rust ownership from the start.” The conversation is kept only in this tab.

0 / 2,000 characters

Enter to send, Shift+Enter for a new line. Do not include personal information.

Chapter 8

Mapping, Filtering, and Reducing Collections

In this chapter, we look at three patterns that turn repetitive list-processing code into short, clear expressions: **map** (transform every item), **where** (keep some items, drop others), and **reduce** or **fold** (combine everything into one value). These patterns exist in almost every modern la
Level: GeneralLength: In depth4 min read

Read at another depth

Mapping, Filtering, and Reducing Collections

In this chapter, we look at three patterns that turn repetitive list-processing code into short, clear expressions: map (transform every item), where (keep some items, drop others), and reduce or fold (combine everything into one value). These patterns exist in almost every modern language, and in Dart they are part of the standard library, so you can use them on any List, Set, or Map keys.

Prerequisites

This chapter assumes you have read the previous chapters on collections and functions. You should know how to create a List, how to write a simple function, and what an anonymous function looks like in Dart, for example (n) => n * 2.

The core idea: functions as values

All three patterns rest on one idea: in Dart, a function can be treated as a value. You can store it in a variable, pass it to another function, and have that function call it. A function that takes another function as an argument is called a higher-order function.

Think of it like a sorting machine on a conveyor belt. The machine (say, map) is fixed, but you choose the stamping tool that goes inside it. You hand the tool over as an argument, and the machine applies it to every item that passes by.

  • map takes a function that converts one item into another item.
  • where takes a function that returns true or false for each item, called a predicate.
  • reduce and fold take a function that combines two values into one.

map: transforming every item

map produces a new sequence where every item has been transformed by your function. It does not change the original list.

Code example in dart
void main() {
  final prices = [1.50, 2.25, 3.00];

  // A named function used as a value.
  double withTax(double price) => price * 1.20;

  final taxed = prices.map(withTax).toList();
  print(taxed); // [1.8, 2.7, 3.6000000000000001]
}

Two details matter here. First, you usually call .toList() on the result, because map returns a lazy Iterable — a sequence that computes items only when you ask for them. That laziness is efficient when you chain steps, but you need a concrete list if you want indexing or .length. Second, notice the odd number at the end: this is floating-point rounding, not a Dart bug. Rounding money in real code is a separate topic.

Most of the time you will write the transformation inline as an anonymous function:

Code example in dart
void main() {
  final names = ['ada', 'alan', 'grace'];
  final upper = names.map((n) => n.toUpperCase()).toList();
  print(upper); // [ADA, ALAN, GRACE]
}

where: keeping only what matches

where filters a sequence, keeping items for which your predicate returns true.

Code example in dart
void main() {
  final words = ['apple', 'fig', 'banana', 'kiwi'];
  final longWords = words.where((w) => w.length >= 4).toList();
  print(longWords); // [apple, banana]
}

where never changes items; it only decides which ones survive. You can chain it with map freely, because both work on any Iterable.

Combining steps: build a pipeline

The real power appears when you chain the steps. Because each step returns an Iterable, you can keep calling methods on the result:

Code example in dart
void main() {
  final names = ['Ada', 'Alan', 'Grace', 'Edsger'];

  final result = names
      .where((n) => n.length > 3)
      .map((n) => n.toUpperCase())
      .toList();

  print(result); // [GRACE, EDSGER]
}

Because map and where are lazy, Dart walks the items through the whole pipeline one item at a time, rather than building a full intermediate list after each step. For large collections, that saves memory and time.

reduce and fold: combining into one value

Sometimes you want a single result: a total, a maximum, a joined string. reduce takes the first item as the starting value, then repeatedly applies your combining function to the running result and the next item.

Code example in dart
void main() {
  final scores = [3, 5, 2, 8];
  final total = scores.reduce((a, b) => a + b);
  print(total); // 18
}

Behind the scenes this computes ((3 + 5) + 2) + 8. The function always receives two arguments: the accumulated value and the next item.

reduce has one catch: on an empty collection it throws an error, because there is no starting value. fold solves this by letting you supply an initial value, and it can also change the result type:

Code example in dart
void main() {
  final scores = [3, 5, 2, 8];
  final sum = scores.fold(0, (a, b) => a + b);       // int
  final labelled = scores.fold<int>(0, (a, b) => a + b);
  final empty = <int>[];
  print(sum);          // 18
  print(labelled);     // 18
  print(empty.fold(0, (a, b) => a + b)); // 0, no crash
}

A common style rule: prefer fold with an explicit initial value when the collection might be empty, and prefer reduce when you are sure there is at least one item and want to start from it.

Dart also provides shortcuts for the most common folds: sum via Iterable.sum (with package collection), any, every, contains, and join:

Code example in dart
void main() {
  final scores = [3, 5, 2, 8];
  print(scores.any((s) => s > 7));        // true
  print(scores.every((s) => s > 0));      // true
  print(['a', 'b'].join('-'));            // a-b
}

Other members of the family

Once you know these three, the rest of the Iterable toolbox follows the same pattern:

  • firstWhere: find the first item matching a predicate, or throw if none matches.
  • take(n) and skip(n): take the first n items, or drop them.
  • expand: map each item to zero or more items (flatten nested lists).
  • forEach: like map, but for side effects such as printing, with no result collected.
Code example in dart
void main() {
  final nested = [[1, 2], [3, 4]];
  print(nested.expand((pair) => pair).toList()); // [1, 2, 3, 4]
}

Common mistakes to avoid

  1. Forgetting .toList() and then trying to index the result. Iterable has no [] operator; call .toList() first.
  2. Calling reduce on a possibly empty list. Use fold with an initial value instead.
  3. Reusing the same variable name for the accumulator and the item in reduce, which makes the logic hard to read. Choose clear names like (total, item).
  4. Using map for side effects like printing. That is what forEach is for.

Exercise

You have a list of order amounts [12.0, 7.5, 30.0]. Write a chain that keeps only amounts above 10, then computes their total. Why is fold a safer choice than reduce here?

Show answer

final total = orders.where((o) => o > 10).fold(0.0, (a, b) => a + b); — fold is safer because you supply an initial value, so the code works even if the filtered list is empty (it returns 0.0), while reduce would throw on an empty list.

Exercise

What is the difference between .map(f).toList() and calling forEach on a list?

Show answer

map builds and returns a new sequence of transformed values (and needs .toList() to become a concrete list), while forEach performs an action for each item and returns nothing. Use map when you want the transformed results; use forEach for side effects like printing.

Spotted a mistake, or code that does not work?

Report an issue

Chapter: Mapping, Filtering, and Reducing Collections

0 / 2,000 characters

Plain text, for example: the second code sample does not compile. Do not include personal information.

You can't do that