On this page25 sections
- Prerequisites
- Connection With Previous Lectures
- Learning Objectives
- 1. Many Values, One Answer
- 2. Counting With where(...).length
- 3. reduce — Combine Two at a Time
- 4. reduce on an Empty List
- 5. fold — Start From an Initial Value
- 6. fold Can Return a Different Type
- 7. The Initial Value Sets the Type
- 8. The Average
- 9. Highest and Lowest by Hand
- 10. Highest and Lowest With reduce
- 11. No .sum or .max Built In
- 12. map and fold Together
- 13. A Real-World Example
- Code Examples
- Common Beginner Mistakes
- Best Practices
- Mini Challenge
- Practice Questions
- Practice Answers
- Quick Revision
- Key Takeaways
- What's Next?
Lecture 44 — List Aggregation
| Category | 7 — Lists |
| Lecture | 044 of 140 |
| Estimated video length | 10 min |
| Target | Dart 3.12.2 |
| Level | Beginner |
Lecture 043 turned a list into another list. Aggregation turns a whole list into one answer — a total, a count, an average, a highest or lowest value.
Contents
Prerequisites · Connection · Objectives · Lesson · Code Examples · Common Mistakes · Best Practices · Mini Challenge · Practice Questions · Answers · Takeaways
Every error message in this lecture is real. Each one was produced by running the code shown on Dart 3.12.2, so you will recognize it when you meet it yourself.
Prerequisites
Students should already understand, from Lectures 001–043:
toStringAsFixed()(Lecture 009) and the conditional operator? :(Lecture 020)for-inloops (Lecture 027)- arrow and anonymous functions (Lecture 034) and higher-order functions (Lecture 036)
length,isEmpty,first, andBad state: No element(Lectures 039–040)whereandmap, and the lazyIterablethey return (Lecture 043)
Connection With Previous Lectures
In Lecture 043, map and where gave us new collections:
var passed = marks.where((mark) => mark >= 50);But a teacher rarely wants another list. She wants one number: the class total, the average, the top mark. So far the only way to get it was a loop with a variable we update by hand. Dart lists have methods that do this job — they combine all the elements into a single value.
Learning Objectives
By the end of this lecture you will be able to:
- Explain what aggregation means
- Count matching elements with
where(...).length - Combine elements with
reduce, and explain why it fails on an empty list - Combine elements with
fold, starting from an initial value of any type - Calculate an average safely
- Find the highest and lowest value by hand and with
reduce - Combine
mapandfoldin one expression - Recognize that
dart:corehas no.sumor.maxon lists
1. Many Values, One Answer
Aggregation means combining many values into one. You already know the loop version:
start a variable at 0, then add each element.
Analogy — think of a shop's cash register. Items pass one by one, and the display shows a single running total. When the last item passes, that total is the answer.
void main() {
var marks = [80, 75, 85, 90];
var total = 0;
for (final mark in marks) {
total += mark;
}
print('Total: $total');
}Total: 330This works, but every total needs the same four lines. The methods below package that pattern.
2. Counting With where(...).length
To count elements that match a condition, filter with where and ask for the length.
void main() {
var marks = [80, 45, 85, 62, 90];
var highMarks = marks.where((mark) => mark >= 80).length;
print('80 or above: $highMarks');
}80 or above: 3where keeps 80, 85, and 90; length counts them.
3. reduce — Combine Two at a Time
reduce takes a function with two parameters. It starts with the first element, then
combines the result so far with the next element, again and again.
void main() {
var marks = [80, 75, 85, 90];
var total = marks.reduce((sum, mark) => sum + mark);
print(total);
}330| Step | sum | mark | Returns |
|---|---|---|---|
| 1 | 80 | 75 | 155 |
| 2 | 155 | 85 | 240 |
| 3 | 240 | 90 | 330 |
The value returned in one step becomes sum in the next. The last value returned is the
answer.
4. reduce on an Empty List
reduce begins with the first element — so an empty list gives it nothing to begin with.
void main() {
var marks = <int>[];
print(marks.reduce((sum, mark) => sum + mark)); // ✗ empty list
}Unhandled exception:
Bad state: No elementThis is the same error as first on an empty list (Lecture 040). With one element,
reduce never calls the function and simply returns that element.
5. fold — Start From an Initial Value
fold takes an initial value first, then the same kind of two-parameter function. The
running result starts at the initial value instead of the first element.
void main() {
var marks = [80, 75, 85, 90];
var empty = <int>[];
var total = marks.fold(0, (sum, mark) => sum + mark);
var nothing = empty.fold(0, (sum, mark) => sum + mark);
print(total);
print(nothing);
}330
0An empty list is no problem: fold just returns the initial value. That makes fold the
safer choice for totals.
6. fold Can Return a Different Type
reduce must return the same type as the elements. fold returns the type of its
initial value, so it can turn a list of names into a number.
void main() {
var names = ['Ali', 'Sara', 'Zayan'];
var letters = names.fold(0, (count, name) => count + name.length);
print('Letters: $letters');
}Letters: 12The same idea with reduce is rejected, because a List<String> must reduce to a String:
void main() {
var names = ['Ali', 'Sara'];
var total = names.reduce((a, b) => a.length + b.length); // ✗
print(total);
}Error: A value of type 'int' can't be returned from a function with return type 'String'.
var total = names.reduce((a, b) => a.length + b.length);
^7. The Initial Value Sets the Type
Dart takes the result type of fold from the initial value. 0 is an int, so adding
double prices to it is rejected:
void main() {
var prices = [1.5, 2.5];
var total = prices.fold(0, (sum, price) => sum + price); // ✗
print(total);
}Error: A value of type 'double' can't be returned from a function with return type 'int'.
var total = prices.fold(0, (sum, price) => sum + price);
^Start from 0.0 instead:
void main() {
var prices = [1.5, 2.5];
var total = prices.fold(0.0, (sum, price) => sum + price);
print(total);
}4.0Store the result in a variable before printing it. print accepts any value, so a fold
written straight inside it is given the vague type Object? — and + does not work on that:
void main() {
var marks = [80, 75];
print(marks.fold(0, (sum, mark) => sum + mark)); // ✗
}Error: The operator '+' isn't defined for the type 'Object?'.
- 'Object' is from 'dart:core'.
Try correcting the operator to an existing operator, or defining a '+' operator.
print(marks.fold(0, (sum, mark) => sum + mark));
^How Dart works out such types is part of generics, which we study in Lecture 072 — Generics Fundamentals.
8. The Average
An average is the total divided by the count. / always gives a double.
void main() {
var marks = [80, 75, 85, 90];
var total = marks.fold(0, (sum, mark) => sum + mark);
print('Average: ${total / marks.length}');
}Average: 82.5On an empty list this becomes 0 / 0, which Dart prints as NaN ("not a number"). Check
isEmpty first:
void main() {
var marks = <int>[];
var total = marks.fold(0, (sum, mark) => sum + mark);
print(total / marks.length);
var average = marks.isEmpty ? 0.0 : total / marks.length;
print(average);
}NaN
0.09. Highest and Lowest by Hand
Start with the first element as both the highest and lowest so far, then compare every element with them.
void main() {
var temps = [21.5, 24.0, 19.0, 26.5, 22.0];
var highest = temps.first;
var lowest = temps.first;
for (final t in temps) {
if (t > highest) highest = t;
if (t < lowest) lowest = t;
}
print('Highest: $highest');
print('Lowest: $lowest');
}Highest: 26.5
Lowest: 19.0Why not start at 0? If every temperature were below zero, 0 would win, and it is not even
in the list.
10. Highest and Lowest With reduce
reduce keeps whichever of two values "wins" and carries it forward.
void main() {
var temps = [21.5, 24.0, 19.0, 26.5, 22.0];
var highest = temps.reduce((a, b) => a > b ? a : b);
var lowest = temps.reduce((a, b) => a < b ? a : b);
print('Highest: $highest');
print('Lowest: $lowest');
}Highest: 26.5
Lowest: 19.0Here reduce fits well: the answer must be one of the elements, and an empty list really has
no highest value — so the error is honest.
11. No .sum or .max Built In
Many languages give lists a ready-made sum or max. Dart's core library does not:
void main() {
var marks = [80, 75, 85];
print(marks.sum); // ✗
}Error: The getter 'sum' isn't defined for the type 'List<int>'.
- 'List' is from 'dart:core'.
Try correcting the name to the name of an existing getter, or defining a getter or field named 'sum'.
print(marks.sum);
^^^.max gives the same error. Such helpers come from extra libraries, which we meet in Lecture
101 — Dart Libraries and Lecture 103 — Dart Packages. With fold and reduce you need none
of them.
12. map and fold Together
map changes each element; fold then combines them. Each item below costs its price plus a
delivery fee of 50:
void main() {
var prices = [500, 1200, 300];
var total = prices
.map((price) => price + 50)
.fold(0, (sum, price) => sum + price);
print('Total: $total');
}Total: 2150map is lazy (Lecture 043); fold asks for each changed value in turn, so no toList() is
needed.
13. A Real-World Example
void main() {
final marks = [72, 45, 88, 30, 65, 91];
var total = marks.fold(0, (sum, mark) => sum + mark);
var average = marks.isEmpty ? 0.0 : total / marks.length;
var highest = marks.reduce((a, b) => a > b ? a : b);
var lowest = marks.reduce((a, b) => a < b ? a : b);
var passed = marks.where((mark) => mark >= 50).length;
print('Total: $total');
print('Average: ${average.toStringAsFixed(1)}');
print('Highest: $highest, Lowest: $lowest');
print('Passed: $passed of ${marks.length}');
}Total: 391
Average: 65.2
Highest: 91, Lowest: 30
Passed: 4 of 6One list, five answers: fold for the total, a guarded division for the average, reduce for
the extremes, and where(...).length for the count.
Code Examples
Example 1 — Sum With fold
void main() {
var numbers = [10, 20, 30];
var sum = numbers.fold(0, (total, n) => total + n);
print(sum);
}60Example 2 — Product With reduce
void main() {
var numbers = [2, 3, 4];
print(numbers.reduce((product, n) => product * n));
}24Example 3 — Counting Long Names
void main() {
var names = ['Ali', 'Sara', 'Zayan', 'Hina'];
print(names.where((name) => name.length > 3).length);
}3Example 4 — The Longest Name
void main() {
var names = ['Ali', 'Sara', 'Zayan', 'Hina'];
var longest = names.reduce((a, b) => a.length >= b.length ? a : b);
print(longest);
}ZayanExample 5 — Initials With fold
void main() {
var names = ['Ali', 'Sara', 'Zayan'];
var initials = names.fold('', (text, name) => text + name[0]);
print(initials);
}ASZThe initial value '' is a String, so the result is a String.
Example 6 — An Empty Cart Is Safe With fold
void main() {
var cart = <int>[];
var total = cart.fold(0, (sum, price) => sum + price);
print('Total: $total');
}Total: 0Example 7 — Student Marks
void main() {
final marks = [78, 92, 85];
var total = marks.fold(0, (sum, mark) => sum + mark);
print('Total: $total');
print('Average: ${total / marks.length}');
print('Highest: ${marks.reduce((a, b) => a > b ? a : b)}');
}Total: 255
Average: 85.0
Highest: 92Example 8 — Shopping Total
void main() {
final prices = [450, 1200, 300, 800];
var total = prices.fold(0, (sum, price) => sum + price);
var expensive = prices.where((price) => price > 500).length;
print('Total: $total');
print('Items over 500: $expensive');
}Total: 2750
Items over 500: 2Example 9 — Bank Balance
void main() {
final transactions = [500, -200, 1000, -50, 300, -120];
var balance = transactions.fold(1000, (sum, amount) => sum + amount);
var deposits = transactions.where((amount) => amount > 0).length;
print('Balance: $balance');
print('Deposits: $deposits');
}Balance: 2430
Deposits: 3How it works
- The initial value
1000is the opening balance, sofoldstarts there, not at0. - Withdrawals are negative, so adding them lowers the balance.
Example 10 — Temperature Week
void main() {
final temps = [21.5, 24.0, 19.0, 26.5, 22.0, 23.5, 20.5];
var sum = temps.fold(0.0, (total, t) => total + t);
var highest = temps.reduce((a, b) => a > b ? a : b);
var lowest = temps.reduce((a, b) => a < b ? a : b);
print('Average: ${(sum / temps.length).toStringAsFixed(1)}');
print('Range: ${highest - lowest}');
}Average: 22.4
Range: 7.5How it works
- The initial value is
0.0because the temperatures aredouble. - The range is the gap between the highest and lowest reading:
26.5 - 19.0.
Common Beginner Mistakes
1. Using reduce on a list that may be empty
// var total = <int>[].reduce((sum, n) => sum + n);Unhandled exception:
Bad state: No elementFor totals, use fold(0, …). For highest or lowest, check isNotEmpty first.
2. Starting fold at 0 for double values
// var total = [1.5, 2.5].fold(0, (sum, price) => sum + price);Error: A value of type 'double' can't be returned from a function with return type 'int'.Start at 0.0.
3. Calling fold directly inside print(...)
Error: The operator '+' isn't defined for the type 'Object?'.Store the result in a variable first, then print the variable.
4. Expecting reduce to change the type
reduce on a List<String> must return a String. To get a number, use fold with a number
as the initial value.
5. Looking for .sum or .max
Error: The getter 'sum' isn't defined for the type 'List<int>'.Write fold or reduce instead.
6. Dividing by the length of an empty list
total / marks.length on an empty list prints NaN. Guard with isEmpty.
7. Starting the highest value at 0
With [-5, -2, -8], a "highest" that starts at 0 stays 0. Start from first, or use
reduce.
Best Practices
- Prefer
foldfor totals and counts — it handles empty lists. - Use
reducewhen the answer is one of the elements and the list cannot be empty. - Match the initial value's type to the answer:
0,0.0, or''. - Name the two parameters for their roles:
(sum, mark), not(a, b), when it helps. - Guard every average with
isEmpty. - Store a
foldresult in a variable before printing it. - Count with
where(...).lengthinstead of a hand-written counter.
Mini Challenge
Zayan's game scores are [12, 7, 15, 9]. Print the total, the best score, and how many
scores are above 10.
Total: 43
Best: 15
Above 10: 2Try it yourself before opening the solution.
Show solution
void main() {
final scores = [12, 7, 15, 9];
print('Total: ${scores.fold(0, (sum, s) => sum + s)}');
print('Best: ${scores.reduce((a, b) => a > b ? a : b)}');
print('Above 10: ${scores.where((s) => s > 10).length}');
}Total: 43
Best: 15
Above 10: 2Practice Questions
Work through these in order — they run from beginner to application to challenge.
Beginner
- Use
foldto add up[5, 10, 15]and print the result. - Use
reduceto multiply[1, 2, 3, 4, 5]together and print the result. - Count the even numbers in
[3, 8, 12, 5, 20]withwhereand print the count.
Application
- Print the average of
[70, 80, 90]. - Use
reduceto print the highest value in[34, 67, 12, 89, 45]. - Find the lowest value in
[34, 67, 12, 89, 45]with a loop, and print it. - Use
foldto count the total number of characters in['Dart', 'is', 'fun']. - Sara buys items priced
[250, 400, 150], two of each. Usemapandfoldto print the total.
Challenge
- Write
double average(List<double> values)that returns0.0for an empty list. Printaverage([18.5, 21.0, 25.5])with two decimals, thenaverage([]). - Ali's transactions are
[500, -200, 1000, -50, 300]. Print the total of the deposits, the total of the withdrawals, and the final balance (starting from0), labelledDeposits:,Withdrawals:, andBalance:.
Practice Answers
Question 1 — Solution
void main() {
var total = [5, 10, 15].fold(0, (sum, n) => sum + n);
print(total);
}30Question 2 — Solution
void main() {
print([1, 2, 3, 4, 5].reduce((product, n) => product * n));
}120Question 3 — Solution
void main() {
print([3, 8, 12, 5, 20].where((n) => n % 2 == 0).length);
}3Question 4 — Solution
void main() {
var marks = [70, 80, 90];
var total = marks.fold(0, (sum, mark) => sum + mark);
print(total / marks.length);
}80.0Question 5 — Solution
void main() {
print([34, 67, 12, 89, 45].reduce((a, b) => a > b ? a : b));
}89Question 6 — Solution
void main() {
var numbers = [34, 67, 12, 89, 45];
var lowest = numbers.first;
for (final n in numbers) {
if (n < lowest) lowest = n;
}
print(lowest);
}12Question 7 — Solution
void main() {
var words = ['Dart', 'is', 'fun'];
var letters = words.fold(0, (count, word) => count + word.length);
print(letters);
}9Question 8 — Solution
void main() {
var prices = [250, 400, 150];
var total = prices
.map((price) => price * 2)
.fold(0, (sum, price) => sum + price);
print(total);
}1600Question 9 — Solution
double average(List<double> values) {
if (values.isEmpty) return 0.0;
var total = values.fold(0.0, (sum, v) => sum + v);
return total / values.length;
}
void main() {
print(average([18.5, 21.0, 25.5]).toStringAsFixed(2));
print(average([]));
}21.67
0.0[] passed to a List<double> parameter becomes an empty List<double>.
Question 10 — Solution
void main() {
final transactions = [500, -200, 1000, -50, 300];
var deposits = transactions.where((t) => t > 0).fold(0, (sum, t) => sum + t);
var withdrawals = transactions
.where((t) => t < 0)
.fold(0, (sum, t) => sum + t);
print('Deposits: $deposits');
print('Withdrawals: $withdrawals');
print('Balance: ${deposits + withdrawals}');
}Deposits: 1800
Withdrawals: -250
Balance: 1550where and fold chain just like map and fold.
Quick Revision
- What does "aggregation" mean?
- How do you count the elements that match a condition?
- Where does
reducestart, and where doesfoldstart? - What happens when you call
reduceon an empty list? - Why does
fold(0, …)fail on a list ofdoublevalues? - Which method can turn a
List<String>into anint? - How do you avoid
NaNwhen averaging? - Does
dart:coregive lists a.sumgetter?
Key Takeaways
- Aggregation combines many elements into one value.
where(condition).lengthcounts matching elements.reducestarts from the first element and must return the element type.reduceon an empty list throwsBad state: No element.foldstarts from an initial value, returns that value for an empty list, and can produce a different type.- The initial value decides
fold's result type: use0.0fordoublevalues. - An average is
total / length; guard against an empty list to avoidNaN. - Highest and lowest: start from
firstin a loop, or usereducewith? :. dart:corehas no.sumor.maxon lists —foldandreducedo the job.maporwherefollowed byfoldtransforms and combines in one expression.
What's Next?
Lecture 45 — Sorting & Reversing Lists. We can now summarize a list, but its elements
are still in whatever order they arrived. Next we put them in order — with sort, with our
own comparison function, and with reversed.