On this page26 sections
- Prerequisites
- Connection With Previous Lectures
- Learning Objectives
- 1. Transforming a List
- 2. forEach — Run a Function for Each Element
- 3. map — A New Value for Each Element
- 4. A Lazy Iterable and toList()
- 5. where — Keep Only the Matches
- 6. whereType — Keep One Type
- 7. expand — Many Values from Each Element
- 8. take and skip — By Count
- 9. takeWhile and skipWhile — By Condition
- 10. Chaining Transformations
- 11. sublist — Copy Part of a List
- 12. join — From a List to a String
- 13. split — From a String to a List
- 14. 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 43 — List Transformation
| Category | 7 — Lists |
| Lecture | 043 of 140 |
| Estimated video length | 12 min |
| Target | Dart 3.12.2 |
| Level | Beginner |
Doubling every price or keeping only the passing marks used to mean an empty list, a loop and an
add. Withmap,whereand their relatives, you describe the change in one line and Dart builds the new list for you.
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–042:
for-inloops andsplit('')on a string (Lecture 027)- arrow and anonymous functions (Lecture 034) and higher-order functions (Lecture 036)
- creating lists,
List<int>, and reading elements by index (Lectures 039–040) add,removeWhere, and theRangeError(Lectures 040–041)- searching a list with a condition (Lecture 042)
Connection With Previous Lectures
To add 10% tax to every price, we can already write this:
void main() {
var prices = [100, 250, 400];
var withTax = <double>[];
for (final price in prices) {
withTax.add(price * 1.1);
}
print(withTax);
}[110.00000000000001, 275.0, 440.00000000000006]Five lines of loop machinery for one idea: "multiply each price by 1.1". Keeping only the passing marks, or the first three names, needs the same machinery again. In Lecture 036 we saw that a higher-order function can hold the "how" while we pass in the "what". Lists come with exactly those functions built in.
Learning Objectives
By the end of this lecture you will be able to:
- Use
forEachand explain why afor-inloop is usually preferred - Build new values from every element with
map - Keep only matching elements with
whereandwhereType - Explain why
mapandwherereturn a lazyIterable, and turn it into a list withtoList() - Take or skip elements with
take,skip,takeWhile, andskipWhile - Chain several transformations into one readable expression
- Copy part of a list with
sublist - Convert between lists and strings with
joinandsplit
1. Transforming a List
Transforming a list means producing a new sequence from it — changed values, fewer values, or both — while the original list stays exactly as it was.
Analogy — think of a photocopier with settings. You put in the original pages and choose "enlarge" or "only odd pages". You get a new stack; the original pages are untouched.
removeWhere from Lecture 041 changes a list. Every method in this lecture leaves it alone
and gives you something new.
2. forEach — Run a Function for Each Element
You met forEach in Lecture 034. It calls your function once per element, in order:
void main() {
var names = ['Ali', 'Sara', 'Zayan'];
names.forEach((name) => print('Hello, $name'));
names.forEach(print);
}Hello, Ali
Hello, Sara
Hello, Zayan
Ali
Sara
ZayanThe second call passes print as a tear-off (Lecture 035) — no parentheses. forEach
returns nothing, so it is not really a transformation: it only does something with each
element. For anything longer than one call, prefer a for-in loop; dart analyze suggests
the same.
3. map — A New Value for Each Element
map calls your function on every element and collects what it returns:
void main() {
var marks = [85, 42, 78];
var bonus = marks.map((mark) => mark + 5);
print(bonus);
print(marks);
}(90, 47, 83)
[85, 42, 78]Every mark got 5 more, and marks is unchanged. But look at the brackets: the result
printed with round brackets, not square ones. It is not a list.
4. A Lazy Iterable and toList()
map returns an Iterable — the family that lists belong to: a sequence you can walk
through with for-in. Round brackets in the output tell you that you are looking at an
Iterable that is not a List. Call toList() to get a real list:
void main() {
var marks = [85, 42, 78];
var bonus = marks.map((mark) => mark + 5).toList();
print(bonus);
print(bonus[0]);
}[90, 47, 83]
90Why not return a list straight away? Because the Iterable is lazy: it does no work
until something asks for its values.
void main() {
var marks = [85, 42];
var doubled = marks.map((mark) {
print('Doubling $mark');
return mark * 2;
});
print('Nothing doubled yet');
print(doubled.toList());
}Nothing doubled yet
Doubling 85
Doubling 42
[170, 84]map only wrote down the recipe. The function ran when toList() asked for the values. If
nothing ever asks, the work never happens. We study Iterable properly in Lecture 046 —
Collection Features.
5. where — Keep Only the Matches
where takes a function that returns a bool and keeps every element for which it returns
true:
void main() {
var marks = [85, 42, 78, 39, 91];
var passed = marks.where((mark) => mark >= 50).toList();
print(passed);
print(marks);
}[85, 78, 91]
[85, 42, 78, 39, 91]It is the opposite of removeWhere: where keeps the matches, and it builds a new list
instead of changing the old one.
6. whereType — Keep One Type
A List<Object> can hold different kinds of values (Lecture 039). whereType<T>() keeps
only the elements of type T, and the result has that type:
void main() {
List<Object> form = ['Sara', 17, 'Lahore', 92.5, 3];
List<int> wholeNumbers = form.whereType<int>().toList();
print(form.whereType<String>());
print(wholeNumbers);
}(Sara, Lahore)
[17, 3]7. expand — Many Values from Each Element
map gives exactly one value per element. expand lets your function return a list for
each element, and joins all those lists into one sequence:
void main() {
var groups = [
['Ali', 'Sara'],
['Zayan'],
];
print(groups.expand((group) => group).toList());
print([1, 2, 3].expand((n) => [n, n * 10]).toList());
}[Ali, Sara, Zayan]
[1, 10, 2, 20, 3, 30]You will need expand less often than map and where; flattening a list of lists is its
most common job.
8. take and skip — By Count
take(n) keeps the first n elements; skip(n) drops them and keeps the rest:
void main() {
var queue = ['Ali', 'Sara', 'Zayan', 'Hina', 'Omar'];
print(queue.take(2).toList());
print(queue.skip(2).toList());
print(queue.take(10).toList());
}[Ali, Sara]
[Zayan, Hina, Omar]
[Ali, Sara, Zayan, Hina, Omar]Asking for more than there is is not an error — take(10) simply stops at the end.
9. takeWhile and skipWhile — By Condition
takeWhile keeps elements while the condition holds and stops at the first one that
fails. skipWhile drops elements while it holds, then keeps everything after:
void main() {
var temps = [18, 21, 24, 31, 22, 35];
print(temps.takeWhile((t) => t < 30).toList());
print(temps.skipWhile((t) => t < 30).toList());
}[18, 21, 24]
[31, 22, 35]22 is below 30, but takeWhile had already stopped at 31. That is the difference from
where, which checks every element.
10. Chaining Transformations
Each method returns an Iterable, so you can call the next method straight on the result.
Put each step on its own line and read from top to bottom:
void main() {
var marks = [85, 42, 78, 39, 91, 66];
var report = marks
.where((mark) => mark >= 50)
.map((mark) => 'Mark: $mark')
.take(3)
.toList();
print(report);
}[Mark: 85, Mark: 78, Mark: 91]Keep the passing marks, turn each into a label, take the first three, make a list. toList()
goes once, at the end of the chain.
11. sublist — Copy Part of a List
sublist(start, end) copies the elements from start up to, but not including, end —
the same rule as removeRange in Lecture 041. Leave out end to copy to the end:
void main() {
var week = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
print(week.sublist(0, 5));
print(week.sublist(5));
}[Mon, Tue, Wed, Thu, Fri]
[Sat, Sun]Square brackets: sublist returns a real List straight away, no toList() needed. Unlike
take, a wrong index crashes:
void main() {
print([1, 2, 3].sublist(1, 5)); // ✗ end is past the last index
}Unhandled exception:
RangeError (end): Invalid value: Not in inclusive range 1..3: 512. join — From a List to a String
join glues the elements into one String, with the text you choose between them:
void main() {
var names = ['Ali', 'Sara', 'Zayan'];
print(names.join(', '));
print(names.join(' -> '));
print([3, 1, 4].join());
}Ali, Sara, Zayan
Ali -> Sara -> Zayan
314With no argument, nothing goes between the elements. join works on any Iterable, so it is
a handy last step of a chain instead of toList().
13. split — From a String to a List
In Lecture 027, split('') broke a string into characters. Give split any separator and it
cuts the string wherever that separator appears, returning a List<String>:
void main() {
var line = 'Milk,Bread,Eggs';
var items = line.split(',');
print(items);
print(items.length);
print('Ali Sara Zayan'.split(' '));
}[Milk, Bread, Eggs]
3
[Ali, Sara, Zayan]split and join are opposites: split turns text into a list, join turns a list back
into text.
14. A Real-World Example
Sara's teacher receives the marks as one line of text:
void main() {
var input = '85,42,78,39,91';
var marks = input.split(',').map(int.parse).toList();
var passed = marks.where((mark) => mark >= 50).toList();
var curved = passed.map((mark) => mark + 5).toList();
print('All marks: $marks');
print('Passed: ${passed.length} of ${marks.length}');
print('Curved: ${curved.join(' | ')}');
print('Top two: ${curved.take(2).toList()}');
}All marks: [85, 42, 78, 39, 91]
Passed: 3 of 5
Curved: 90 | 83 | 96
Top two: [90, 83]split makes a List<String>, map(int.parse) turns each piece into an int (Lecture 010),
where keeps the passes, map adds the curve, and join and take shape the report. The
marks list never changes.
Code Examples
Example 1 — Greeting Every Guest With forEach
void main() {
var guests = ['Ali', 'Sara'];
guests.forEach((guest) => print('Welcome, $guest!'));
}Welcome, Ali!
Welcome, Sara!Example 2 — Squares With map
void main() {
var numbers = [1, 2, 3, 4];
var squares = numbers.map((n) => n * n).toList();
print(squares);
}[1, 4, 9, 16]Example 3 — Names in Capitals
void main() {
var names = ['ali', 'sara', 'zayan'];
print(names.map((name) => name.toUpperCase()).toList());
}[ALI, SARA, ZAYAN]Example 4 — Even Numbers With where
void main() {
var numbers = [3, 8, 12, 5, 20, 7];
print(numbers.where((n) => n % 2 == 0).toList());
}[8, 12, 20]Example 5 — The Middle of a List
void main() {
var scores = [10, 20, 30, 40, 50];
print(scores.skip(1).take(3).toList());
print(scores.sublist(1, 4));
}[20, 30, 40]
[20, 30, 40]Two routes to the same result: skip/take count elements, sublist uses indexes.
Example 6 — Words to a Sentence and Back
void main() {
var sentence = 'Dart is fun';
var words = sentence.split(' ');
print(words);
print(words.join('_'));
}[Dart, is, fun]
Dart_is_funExample 7 — Student Marks: Grade Labels
void main() {
var marks = [92, 67, 45, 81];
var grades = marks
.map((mark) => mark >= 80 ? 'A' : (mark >= 50 ? 'B' : 'F'))
.toList();
print('Marks: $marks');
print('Grades: ${grades.join(', ')}');
}Marks: [92, 67, 45, 81]
Grades: A, B, F, AHow it works
mapturns eachintinto aString, sogradesis aList<String>.- The conditional expression from Lecture 023 picks the grade for each mark.
Example 8 — Shopping Total: Prices With Tax
void main() {
var prices = [200, 450, 1200];
var withTax = prices.map((price) => price * 1.1).toList();
var labels = withTax.map((p) => 'Rs ${p.toStringAsFixed(2)}');
print(labels.join('\n'));
}Rs 220.00
Rs 495.00
Rs 1320.00How it works
- The first
mapmakes aList<double>; the originalpricesstay asint. - The second
mapformats each price withtoStringAsFixed(2)(Lecture 009). join('\n')puts each label on its own line — notoList()needed.
Example 9 — Bank Balance: Deposits and Withdrawals
void main() {
var transactions = [500, -200, 1000, -50, 300];
var deposits = transactions.where((t) => t > 0).toList();
var withdrawals = transactions.where((t) => t < 0).map((t) => -t).toList();
print('Deposits: $deposits');
print('Withdrawals: $withdrawals');
}Deposits: [500, 1000, 300]
Withdrawals: [200, 50]How it works
- The first
wherekeeps positive amounts. - The second chain keeps negative amounts, then
mapflips their sign for display. - Adding these up into a total is the topic of the next lecture.
Example 10 — Temperature: Celsius Readings to Fahrenheit
void main() {
var celsius = [21.0, -40.0, 30.0, 99.0];
var fahrenheit = celsius
.where((c) => c > -30 && c < 60)
.map((c) => c * 9 / 5 + 32)
.toList();
print(fahrenheit);
}[69.8, 86.0]How it works
wheredrops the two readings that look like sensor faults.mapconverts the rest withc * 9 / 5 + 32.toList()comes once, at the end of the chain.
Common Beginner Mistakes
1. Storing a map result in a List variable
void main() {
var marks = [85, 42, 78];
// List<int> bonus = marks.map((m) => m + 5); // ✗
}Error: A value of type 'Iterable<int>' can't be assigned to a variable of type 'List<int>'.Add .toList() at the end of the chain.
2. Using an index on an Iterable
void main() {
var marks = [85, 42, 78];
// print(marks.map((m) => m * 2)[0]); // ✗
}Error: The operator '[]' isn't defined for the type 'Iterable<int>'.Only a List has [index]. Call toList() first.
3. Expecting map to change the original list
void main() {
var marks = [85, 42, 78];
marks.map((m) => m * 2); // result thrown away
print(marks);
}[85, 42, 78]No error, no change — and because the Iterable is lazy, the function never even ran. Store
the result: var doubled = marks.map((m) => m * 2).toList();.
4. Returning a non-bool from where
void main() {
var marks = [85, 42, 78];
// marks.where((m) => m + 1); // ✗
}Error: A value of type 'int' can't be returned from a function with return type 'bool'.The function must answer yes or no: (m) => m > 50.
5. Using the result of forEach
void main() {
var marks = [85, 42, 78];
// var result = marks.forEach((m) => m * 2);
// print(result); // ✗
}Error: This expression has type 'void' and can't be used.forEach returns nothing. To collect new values, use map.
6. Confusing takeWhile with where
[18, 31, 22].takeWhile((t) => t < 30) gives [18], not [18, 22] — it stops at the first
failure. Use where to check every element.
Best Practices
- Use
mapto change values,whereto choose them — and afor-inloop to simply do something with each element. - Call
toList()once, at the end of a chain, and only when you need a real list. - Put each step of a long chain on its own line.
- Keep the functions you pass short and free of side effects such as
print. - Use
sublistwhen you know indexes,take/skipwhen you know counts. - Finish with
joinwhen the goal is text.
Mini Challenge
Zayan's class list arrives as 'zayan,ali,sara,hina'. Split it, keep names with more than
three letters, capitalize them, and print them joined with ' & '.
ZAYAN & SARA & HINATry it yourself before opening the solution.
Show solution
void main() {
var input = 'zayan,ali,sara,hina';
var result = input
.split(',')
.where((name) => name.length > 3)
.map((name) => name.toUpperCase())
.join(' & ');
print(result);
}ZAYAN & SARA & HINAPractice Questions
Work through these in order — they run from beginner to application to challenge.
Beginner
- Use
forEachto printItem: <name>for each of['Pen', 'Book', 'Bag']. - Use
mapto triple every number in[2, 4, 6]and print the result as a list. - Use
whereto keep the numbers greater than10from[5, 15, 8, 22, 10]and print them as a list. - Print
[1, 2, 3].map((n) => n + 1)withouttoList(), then with it. What differs?
Application
- From
['Ali', 'Sara', 'Zayan', 'Hina', 'Omar'], print the first three names and then the rest, each as a list. - From
List<Object> data = [10, 'ten', 20, 'twenty', 30], print only theStringvalues as a list. - Split
'red;green;blue'into a list, print it, then join it back with' / '. - From
[45, 67, 89, 30, 72], keep marks of at least50, add3to each, and print the result as a list — in one chain.
Challenge
- Temperatures arrive as
'19,23,28,33,26,36'. Print the readings before the first one above30, then the readings from that one onwards. - From
[1200, 300, 4500, 800, 150], keep prices over500, apply a 10% discount, and print them asRs 1080.0, Rs 4050.0, Rs 720.0.
Practice Answers
Question 1 — Solution
void main() {
['Pen', 'Book', 'Bag'].forEach((item) => print('Item: $item'));
}Item: Pen
Item: Book
Item: BagQuestion 2 — Solution
void main() {
print([2, 4, 6].map((n) => n * 3).toList());
}[6, 12, 18]Question 3 — Solution
void main() {
print([5, 15, 8, 22, 10].where((n) => n > 10).toList());
}[15, 22]10 is not greater than 10, so it is left out.
Question 4 — Solution
void main() {
print([1, 2, 3].map((n) => n + 1));
print([1, 2, 3].map((n) => n + 1).toList());
}(2, 3, 4)
[2, 3, 4]Round brackets mean a lazy Iterable; square brackets mean a List.
Question 5 — Solution
void main() {
var names = ['Ali', 'Sara', 'Zayan', 'Hina', 'Omar'];
print(names.take(3).toList());
print(names.skip(3).toList());
}[Ali, Sara, Zayan]
[Hina, Omar]Question 6 — Solution
void main() {
List<Object> data = [10, 'ten', 20, 'twenty', 30];
print(data.whereType<String>().toList());
}[ten, twenty]Question 7 — Solution
void main() {
var colors = 'red;green;blue'.split(';');
print(colors);
print(colors.join(' / '));
}[red, green, blue]
red / green / blueQuestion 8 — Solution
void main() {
var marks = [45, 67, 89, 30, 72];
var result = marks.where((m) => m >= 50).map((m) => m + 3).toList();
print(result);
}[70, 92, 75]Question 9 — Solution
void main() {
var temps = '19,23,28,33,26,36'.split(',').map(int.parse).toList();
print(temps.takeWhile((t) => t <= 30).toList());
print(temps.skipWhile((t) => t <= 30).toList());
}[19, 23, 28]
[33, 26, 36]26 appears in the second list: skipWhile stops skipping at 33 and keeps everything after.
Question 10 — Solution
void main() {
var prices = [1200, 300, 4500, 800, 150];
var result = prices
.where((p) => p > 500)
.map((p) => p * 0.9)
.map((p) => 'Rs $p')
.join(', ');
print(result);
}Rs 1080.0, Rs 4050.0, Rs 720.0Quick Revision
- What does
forEachreturn? - What is the difference between
mapandwhere? - What do round brackets in printed output tell you?
- What does "lazy" mean for the result of
map? - How is
takeWhiledifferent fromwhere? - Does
sublistneedtoList()? Why or why not? - Which method turns a list into a
String, and which turns aStringinto a list?
Key Takeaways
- Transformation methods build something new; the original list never changes.
forEachruns a function for each element and returns nothing — preferfor-in.mapproduces one new value per element;wherekeeps the elements that pass a test.whereType<T>()keeps only elements of typeT;expandturns each element into many.take/skipwork by count;takeWhile/skipWhilestop at the first element that fails.map,whereand friends return a lazyIterable, printed with( );toList()makes a realList.- Chain steps one per line, with
toList()orjoinonce at the end. sublist(start, end)returns aListcopy and throws aRangeErrorfor bad indexes.jointurns a list into aString;splitturns aStringinto aList<String>.
What's Next?
Lecture 44 — List Aggregation. Every method here turned a list into another sequence. Often
we want just one answer instead — the total of a shopping cart, the highest mark, the
balance after all transactions. Next we combine a whole list into a single value with
reduce, fold, and their relatives.