Dart from the ground up

Lists and maps

Keeping many values in a list and many lookups in a map, reading them by position or by key, and the handful of calls that add, remove, find and order what is inside.

Part 7 of 12

Intermediate19 min readBack to Courses

A list keeps values in order

A list literal is the values in square brackets: final names = <String>["Ada", "Grace"]; The <String> in front says what is inside, which is worth writing whenever the list starts empty so Dart knows what belongs there. Positions are counted from zero, so names[0] is the first value.

Lists know how to change. names.add("Alan") puts a value on the end, names.removeAt(0) takes the first one out and shifts the rest down, names.indexOf("Grace") gives a position or minus one when the value is not there, and names.contains("Ada") answers yes or no. names.length is how many you have.

Ordering a list

sort puts the list in order, and it changes the list itself rather than handing back a new one. Called with no argument it uses the natural order of the values, so a list of text comes out alphabetically.

For anything else, hand it a rule: cents.sort((a, b) => a - b) orders numbers from the smallest up, and cents.sort((a, b) => b - a) from the largest down. The rule takes two values and answers a number: negative means the first one comes first, positive means the second one does, and zero means they are level.

A map keeps a value per key

A map literal pairs each key with a value: final prices = <String, int>{"hub": 3200, "cable": 450}; You read a value with its key in square brackets, prices["hub"], and you add one by assigning, prices["mouse"] = 900;. Keys are unique, so writing to a key that already exists replaces its value.

prices.containsKey("mouse") asks whether a key is there, prices.length counts the pairs, and prices.keys.toList() and prices.values.toList() give the two halves. A map literal keeps its keys in the order you wrote them, so printing them is predictable.

A name for a list is a reference

final names = <String>["Ada"]; makes the name final, not the list. The name will always point at that list, and the list itself can still grow and shrink. That is why names.add("Alan") is allowed even though the variable is final.

Assigning the list to a second name copies the reference, not the values. final alias = names; gives two names for one list, and an add through either is visible through both. When you want an independent list, build a new one: final copy = <String>[...names];

Passing a list to a function follows the same rule. The function can change the contents of your list. If that is not what you meant, return a new list and let the caller decide what to do with it.

Maps that count, and the calls around them

A map is the right shape for counting: the key is the thing, and the value is how many times you have seen it. The idiom is counts[item] = (counts[item] ?? 0) + 1; — read the old count, use zero when the key is new, and store the sum back under the same key.

putIfAbsent adds a key only when it is missing, which is how a map can start every new item at zero. remove takes a pair out and answers its value, or null when the key was not there. update changes an existing value and can supply a default when it is missing.

keys and values are views rather than copies. They reflect the map as it is now, so convert with .toList() before sorting or before changing the map underneath. A map literal keeps insertion order, but sorting the keys is how a report becomes predictable.

A list that changes, and a map to look up

void main() {
  final names = <String>["Ada", "Grace"];
  names.add("Alan");
  print(names);
  print(names.indexOf("Grace"));
  final prices = <String, int>{"hub": 3200};
  prices["cable"] = 450;
  print(prices["cable"]);
  print(prices.containsKey("mouse"));
  print(prices.keys.toList());
}

A list of names and a price list

Run it and follow the list from top to bottom. Then add a fourth name, remove the second one with removeAt, print the list again, and add a second key to the map before printing its keys.

Output
Click "Run" to execute your code

Three collection mistakes. Reading names[names.length], one past the last valid position. Expecting removeAt to leave the following positions alone — they all shift down. And using a value from a map without allowing for null: a miss gives null, not zero, so a lookup that feeds arithmetic needs ?? or a check.

Try these next

  • Count the words in a list of six names with a map, then print the keys in alphabetical order.
  • Copy a list, add to the copy, and print both lengths to prove they are separate.
  • Remove a key from a map, print the value remove hands back, then print the map length.

A basket that counts its items

Run it and check every count against the original list. Then add two more names, one of them new, and remove the hub with `remove`, printing the length once more.

Output
Click "Run" to execute your code

Ask a map for a key it does not have and you get null rather than a crash. That is Dart offering to let you decide what a missing value means, and chapter eleven shows how to handle it properly.

Check what you learned

One question at a time. A wrong answer explains itself — take another go.

Part 1 of 5

Which position does the first value in a list sit at?