Chapter 3 · Data
Maps and sets
Step 1 of 21 · Concept
A Map looks values up by key
By the end of this lesson you look values up by key, count things, and keep each value once.
const ages = new Map();
ages.set("Ada", 36);
ages.set("Alan", 41);
A Map holds keys, each with its value.
| write | does |
|---|---|
ages.set(k, v) | puts v under k; again, it replaces the value |
ages.get(k) | the value, or undefined when k is missing |
ages.has(k) | true when k is there |
ages.delete(k) | removes k and its value |
ages.size | how many keys |
Step 2 of 21 · Concept
How console.log shows a Map
| value | console.log shows |
|---|---|
| an array | [ 'Ada', 'Alan' ] |
| an object | { name: 'Ada', age: 36 } |
| a Map | Map(2) { 'Ada' => 36, 'Alan' => 41 } |
A Map shows its size first, then each key, =>, and its value.
Step 3 of 21 · Quiz
Which line is the Map?
What does this program log?
const stock = new Map();
stock.set("tea", 2);
console.log(stock);
Step 4 of 21 · Quiz
Size and a missing key
What does this program log?
const ages = new Map();
ages.set("Ada", 36);
ages.set("Alan", 41);
ages.set("Ada", 37);
console.log(ages.size, ages.get("Grace"));
Step 5 of 21 · Demo
See it run
Run it and compare with your answer. Then log ages.get("Ada") and ages.has("Grace") too.
Step 6 of 21 · Concept
A Map keeps its order
A Map keeps its keys in the order they were added. Numbers too: an object puts whole-number keys first (lesson 11), a Map does not.
- A key set again keeps its place.
- A key deleted and added again goes last.
A for...of loop gives each key with its value, as a pair:
for (const [name, age] of ages) {
console.log(name, age);
}
Step 7 of 21 · Demo
A Map, printed and looped
Run it: Ada keeps her place after the change. Then add ages.delete("Ada"); before the last set and run it again.
Step 8 of 21 · Exercise
Your turn: a shop's stock
Sell each item of sales, one at a time:
- When the shop has none of it, log
Noand the item. - Else take 1 from its count.
- At the end, log each item and its count.
The program must print:
No cake
No juice
tea 2
cake 0
The shop has no juice key: get gives undefined, and undefined - 1 is NaN. Ask has first.
In a for...of over sales, one if with an else: none left when the Map does not have the item, or its count is 0. Then a second loop over stock.
if (!stock.has(item) || stock.get(item) === 0) {
Step 9 of 21 · Concept
Counting with a Map
const counts = new Map();
for (const w of "the cat the".split(" ")) {
counts.set(w, (counts.get(w) ?? 0) + 1);
}
Line 3 counts one word:
- The first time a word comes,
getgivesundefined.??turns that into0(lesson 11), so the count is set to 1. - Each later time,
getgives the count so far, and the word gets 1 more.
Step 10 of 21 · Trace
Watch the counts
counts holds an arrow to the Map on the heap. Press Next to count one word at a time.
const counts = new Map();
for (const w of "the cat the".split(" ")) {
counts.set(w, (counts.get(w) ?? 0) + 1);
}
console.log(counts);- The loop starts:
wis"the"and the Map is empty. Line 3 is next. getgaveundefined, so"the"is set to 1. The loop takes the next word.wis"cat", new to the Map: it is set to 1."the"comes again:getgave 1, so it is set to 2. No words are left; line 5 is next.- Line 5 logged the Map. The program is finished.
Step 11 of 21 · Quiz
The last count
At the end, what does counts.get("the") give?
const counts = new Map();
for (const w of "the cat the".split(" ")) {
counts.set(w, (counts.get(w) ?? 0) + 1);
}
console.log(counts);Step 12 of 21 · Concept
The most common first
[...counts] turns a Map into an array of [key, value] pairs:
console.log([...counts]);
This logs [ [ 'the', 2 ], [ 'cat', 1 ] ]. In a pair, [0] is the key and [1] the count. So a comparator on
[1] puts the biggest count first (lesson 12):
const top = [...counts].sort((a, b) => b[1] - a[1]);
Equal counts keep their order: first seen, first shown.
Step 13 of 21 · Demo
The top words
Run it: cat and hat tie, so they keep their order. Then change line and run it again.
Step 14 of 21 · Exercise
Your turn: count the input's words
The input is one line of words. Count them:
- Cut the text into words.
- Count each word with a Map.
- Log each word and its count, the most common first.
The program must print:
red 3
blue 2
green 1
The input ends with \n, so its last word is "red\n", not "red", and gets its own count. trim the text before you split it.
split(" ") gives the words. Count them as the top words demo does, then sort [...counts] by [1], highest first.
for (const w of text.trim().split(" ")) {
Step 15 of 21 · Concept
A Set keeps each value once
A Set holds values, each only once, in the order they were first added.
| write | does |
|---|---|
new Set(xs) | a Set of the array's items |
s.add(x) | adds x, unless it is there |
s.has(x), s.size | as a Map's |
[...new Set(xs)] | the array without repeats |
console.log(new Set([3, 1, 3, 2])) shows Set(3) { 3, 1, 2 }.
Step 16 of 21 · Quiz
How many tags?
Type the missing line.
const tags = ["js", "web", "js", "css", "web"];
const unique = new Set(tags);
unique.add("js");
console.log(unique.size);
console.log([...unique].join(" "));
Step 17 of 21 · Demo
See it run
Run it and compare with your answer. Then log unique.has("css") too.
Step 18 of 21 · Concept
Which one when?
| use | for |
|---|---|
| an array | items in order, found by position |
| an object | a record whose property names you know |
| a Map | lookups by any key, and counting |
| a Set | asking "is it there?", and dropping repeats |
Step 19 of 21 · Check yourself
Check yourself: two clubs
- Make a Set of the music club's names.
- Log the chess players who are in that Set too.
- Log how many people are in at least one club:
addevery name of both clubs to a new Set.
The output must be:
Both: Ada, Cy
Members: 4
filter from lesson 12 keeps the chess names its callback says true to; the callback asks the Set. For the count, a Set's size ignores the repeats.
Step 20 of 21 · Challenge · optional
Challenge: names by first letter
Optional.
Group the names by their first letter, in a Map from each letter to an array of names. The program must print:
A: Ada, Alan
B: Bo, Bea
C: Cy
A Map's value can be an array. The first time a letter comes, give it an empty array; each name then goes into its letter's array.
groups.get(letter) gives the array itself, on the heap, so push on it changes the array the Map holds. No set is needed after the first.
if (!groups.has(letter)) {
groups.set(letter, []);
}
groups.get(letter).push(name);
Step 21 of 21 · Recap
Recap
- A Map holds keys and their values:
set,get(undefinedwhen missing),has,delete,size. - Maps and Sets keep the order things were added;
for (const [k, v] of m)loops over a Map. - Count with
m.set(w, (m.get(w) ?? 0) + 1); sort the counts with[...m].sort((a, b) => b[1] - a[1]). - A Set keeps each value once:
[...new Set(xs)]drops repeats.
Next: lesson 14 writes functions that call themselves: recursion.
Review
4 questions from this lesson, in a new order each time. Answer from memory: 3 of 4 right on the first try passes. On a day after you finish the lesson, a pass earns 15 XP.
The review needs JavaScript.
const ages = new Map();
ages.set("Ada", 36);
ages.set("Alan", 41);
ages.set("Ada", 37);
console.log(ages.size, ages.get("Grace"));
const ages = new Map();
ages.set("Ada", 36);
ages.set("Alan", 41);
ages.set("Grace", 85);
ages.set("Ada", 37);
console.log(ages);
for (const [k, v] of ages) {
console.log(`${k} is ${v}`);
}
const stock = new Map();
stock.set("tea", 3);
stock.set("cake", 1);
const sales = ["tea", "cake", "cake", "juice"];
// 1. each sale: none left? log No and the item
// 2. else take 1 from its count
// 3. log each item and its count
const line = "the cat the hat the";
const counts = new Map();
for (const w of line.split(" ")) {
counts.set(w, (counts.get(w) ?? 0) + 1);
}
const top = [...counts].sort((a, b) => b[1] - a[1]);
for (const [w, n] of top) {
console.log(w, n);
}
import { readFileSync } from "node:fs";
const text = readFileSync(0, "utf8");
// 1. cut the text into words
// 2. count each word with a Map
// 3. log each word and its count, most first
const tags = ["js", "web", "js", "css", "web"];
const unique = new Set(tags);
unique.add("js");
console.log(unique.size);
console.log([...unique].join(" "));
const chess = ["Ada", "Bob", "Cy"];
const music = ["Cy", "Di", "Ada"];
// 1. a Set of the music club's names
// 2. the chess players in that Set too
// 3. how many people in at least one club
const names = ["Ada", "Alan", "Bo", "Bea", "Cy"];
const groups = new Map();
// add each name to its first letter's array
for (const [letter, list] of groups) {
console.log(`${letter}: ${list.join(", ")}`);
}