Iterables and Generators
Prerequisites: Collection methods, Classes and objects
An Iterable is anything you can loop over with for-in. Lists and
sets are iterables, and so are the results of map and where.
Lazy iterables
Iterables returned by map, where, take, and friends are lazy:
they do no work until you use them, and they stop as soon as they have
enough:
void main() {
final result = [1, 2, 3, 4]
.map((n) {
print('map $n');
return n * 2;
})
.where((n) {
print('where $n');
return n > 2;
})
.first;
print(result);
}
map 1
where 2
map 2
where 4
4
Only two elements were processed. Calling .toList() would have
processed all four.
Generators: sync*
A function marked sync* returns an iterable, producing values one at a
time with yield:
Iterable<int> fibonacci() sync* {
var (a, b) = (0, 1);
while (true) {
yield a;
(a, b) = (b, a + b);
}
}
Iterable<int> countdown(int from) sync* {
for (var i = from; i >= 1; i--) {
yield i;
}
}
void main() {
print(fibonacci().take(10).toList());
print(countdown(3).toList());
}
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
[3, 2, 1]
fibonacci contains an infinite loop, but the program doesn't hang:
values are only computed as they're requested.
Sorting by a key
class Order {
final String customer;
final double total;
Order(this.customer, this.total);
}
void main() {
final orders = [Order('Linus', 45), Order('Ada', 120), Order('Grace', 300)];
orders.sort((a, b) => b.total.compareTo(a.total));
print(orders.map((o) => o.customer).toList());
final byName = [...orders]..sort((a, b) => a.customer.compareTo(b.customer));
print(byName.map((o) => o.customer).toList());
}
[Grace, Ada, Linus]
[Ada, Grace, Linus]
sort changes the list in place; [...orders]..sort(...) sorts a copy.
The popular package:collection adds helpers such as groupBy,
sortedBy, and firstWhereOrNull.
Common mistakes
- Iterating a lazy iterable several times, repeating the work each time.
Call
toList()once if you'll use the result repeatedly.