Map and Set
Plain objects can be used as lookup tables, and for a long time JavaScript had nothing else. Now it has two collections made for these jobs: Set and Map.
A Set is a group of values with no duplicates. If you add a value that is already in the set, nothing happens.
const colors = new Set(["red", "blue", "red"]);
colors.add("green");
colors.add("blue");
console.log(colors);
console.log(colors.size);
console.log(colors.has("red"));
console.log(colors.has("pink"));
Output:
Set(3) { 'red', 'blue', 'green' }
3
true
false
new Set(...) creates a set. You will learn more about the word new in the Classes section. If you give it an array, each value goes into the set once, so the second "red" is dropped. size is the number of values (a set has size, not length). has checks if a value is in the set.
A set is a quick way to remove duplicates. To get an array back, spread the set into square brackets. Then you can sort it and join it like any array:
<!-- continues -->const list = [...colors];
console.log(list);
console.log(list.toSorted().join(" "));
Output:
[ 'red', 'blue', 'green' ]
blue green red
A Map stores key and value pairs, like an object. You use methods to work with it: set stores a value, get reads one, and has checks if a key exists.
const ages = new Map();
ages.set("Alice", 34);
ages.set("Bob", 41);
ages.set(7, "a number key");
console.log(ages);
console.log(ages.get("Bob"));
console.log(ages.get("Zara"));
console.log(ages.size);
Output:
Map(3) { 'Alice' => 34, 'Bob' => 41, 7 => 'a number key' }
41
undefined
3
get gives undefined for a key that is not there. Unlike an object, a Map keeps a number key as a number (7, not '7'). It also always keeps the keys in the order you added them, even number keys.
You can loop over a Map directly with for...of. Each item is a [key, value] pair, so you can destructure it:
for (const [name, age] of ages) {
console.log(`${name}: ${age}`);
}
Output:
Alice: 34
Bob: 41
7: a number key
A very common job for a Map is counting how often each value appears. For each word, get the count so far, add one, and store it back. The first time you see a word, get gives undefined, so ?? 0 starts the count at zero:
const words = ["go", "stop", "go"];
const counts = new Map();
for (const word of words) {
counts.set(word, (counts.get(word) ?? 0) + 1);
}
console.log(counts);
Output:
Map(2) { 'go' => 2, 'stop' => 1 }
So which one should you use? Use a plain object for a record with fixed, known keys, like an employee with a name and an age. Use a Map when keys are added and counted while the program runs. Use a Set when you only care whether a value is there, or how many different values there are.
In the exercise, count the different words with a Set, then count each word with a Map.