Card 09/ 10
Practice: Counting Words in a Short Review
Count each word's appearances, then list them in the order they first appeared.
Counting how often each word appears in a text practises the three choices a hash map asks for: what the key is, what the value is, and whether the map keeps the order its keys arrived in. This card covers that count as an exercise, with an answer to check yours against.
A hotel review reads: the room was clean and the bed was soft but the room was small.
- Say what the map's keys and values are, and what counting one word costs.
- Give each word's count, listed in the order the words first appear.
- Name the built-in map that gives that order with no extra work in Python, in Java and in TypeScript, and say what Java's
HashMapwould print instead.
An answer: a map from word to count, the nine counts in order, and the map to use in each language
The keys are the words and the values are their counts. For each word the program looks it up, adds one to the count it finds or starts from 1, and stores the result: one lookup and one insert, each O(1) expected, so a text of n words takes O(n) expected. Python's dict and TypeScript's Map keep keys in the order they were first inserted, and in Java a LinkedHashMap does. Each listing was run, and the highlighted lines update the count.
review = "the room was clean and the bed was soft but the room was small"
counts: dict[str, int] = {}
for word in review.split():
counts[word] = counts.get(word, 0) + 1
print(" ".join(f"{word}={count}" for word, count in counts.items()))the=3 room=2 was=3 clean=1 and=1 bed=1 soft=1 but=1 small=1Java's merge stores 1 for a word not yet counted and otherwise adds 1 to its count. The listing fills a HashMap and a LinkedHashMap the same way, to compare their order.
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.stream.Collectors;
public class CountWords {
static String describe(Map<String, Integer> counts) {
return counts.entrySet().stream()
.map(entry -> entry.getKey() + "=" + entry.getValue())
.collect(Collectors.joining(" "));
}
public static void main(String[] args) {
String review = "the room was clean and the bed was soft but the room was small";
Map<String, Integer> hashed = new HashMap<>();
Map<String, Integer> linked = new LinkedHashMap<>();
for (String word : review.split(" ")) {
hashed.merge(word, 1, Integer::sum);
linked.merge(word, 1, Integer::sum);
}
System.out.println("HashMap: " + describe(hashed));
System.out.println("LinkedHashMap: " + describe(linked));
}
}HashMap: small=1 but=1 bed=1 the=3 and=1 was=3 clean=1 room=2 soft=1
LinkedHashMap: the=3 room=2 was=3 clean=1 and=1 bed=1 soft=1 but=1 small=1In TypeScript, get returns undefined for a word not yet counted, and ?? 0 turns that into 0 before the 1 is added.
const review = "the room was clean and the bed was soft but the room was small";
const counts = new Map<string, number>();
for (const word of review.split(" ")) {
counts.set(word, (counts.get(word) ?? 0) + 1);
}
console.log([...counts].map(([word, count]) => `${word}=${count}`).join(" "));the=3 room=2 was=3 clean=1 and=1 bed=1 soft=1 but=1 small=1The counts, in order of first appearance, are the=3, room=2, was=3, clean=1, and=1, bed=1, soft=1, but=1 and small=1. A word seen again updates its value and keeps its place, so room stays second. Java's HashMap held the same counts but listed them in its bucket order, starting with small, which is neither the order of the text nor alphabetical.
← → move through sections and cards