What it is
TypeScript has no separate dictionary type. A plain object whose keys are strings and whose values share one type does the job. Its type is written { [key: string]: number }: any string key, a number value. To work with every entry, you loop over the keys and read each value with its key.
Two ways to loop
The first way is for...in, which gives one key per turn. Inside the body, square brackets read the value:
const stock: { [key: string]: number } = {"apples": 5, "pears": 2};
for (const fruit in stock) {
console.log(fruit, stock[fruit]);
}
It prints:
apples 5
pears 2
The second way gets the key and the value together. Object.entries(stock) returns an array of [key, value] pairs, and for...of with a destructuring pattern unpacks each pair:
for (const [fruit, amount] of Object.entries(stock)) {
console.log(fruit, amount);
}
This prints the same two lines. Use whichever reads better: for...in when you need mostly the key, Object.entries when you use both parts all the time.
Traps
inandofare different.for (const x of stock)on a plain object fails. The compiler reports that the type must have a[Symbol.iterator]()method, and the program that tsc still writes stops withTypeError: stock is not iterable. Usefor...inon the object, orfor...ofonObject.entries(stock).for...ingives keys, not values. The loop variable holds"apples", not5. The value isstock[fruit].- The order is not always the order you typed. Text keys come out in the order they were added, but keys that look like whole numbers (such as
"2"or"10") come first, in ascending order. For{"b": 1, "10": 2, "a": 3, "2": 4}the keys come out as2,10,b,a. Code that only totals or counts gives the same answer in any order, so it never depends on this.
Changing values while you loop
The value you read with stock[fruit] can also be assigned. Changing the value of a key that already exists, while looping over the keys, is fine:
let items = 0;
for (const fruit in stock) {
items += stock[fruit];
}
console.log("items in stock: " + items);
for (const fruit in stock) {
stock[fruit] += 1;
}
console.log(stock["apples"], stock["pears"]);
With the two fruits above it prints:
items in stock: 7
6 3
To know how many entries there are without a loop, use Object.keys(stock).length, which is 2 here.
The exercise
The lesson keeps each player's points in scores. It asks for the total of all points, and for how many players have 10 points or more. The variables total and count start at 0.
- Write a
for...inloop withconst nameoverscores. - Inside it, add
scores[name]tototal. - Still inside, test
scores[name] >= 10and add 1 tocountwhen it is true.
const scores: { [key: string]: number } = {"Ana": 12, "Ben": 7, "Cleo": 15, "Dan": 9};
let total: number = 0;
let count: number = 0;
for (const name in scores) {
total += scores[name];
if (scores[name] >= 10) {
count += 1;
}
}
console.log(total);
console.log(count);
The output is:
43
2
The total is 12 + 7 + 15 + 9 = 43, and only Ana and Cleo reach 10.
A common mistake in the exercise
Writing total += name adds the key, not the player's points. The compiler reports that a string is not assignable to number, and the JavaScript it still writes joins the text instead of adding: with only Ana and Ben in scores it prints 0AnaBen. The points are always scores[name].
Key takeaways
for (const key in obj)visits every key;obj[key]reads its value.Object.entries(obj)withfor...ofgives[key, value]pairs.for...ofstraight on a plain object does not work.- Totals and counts don't depend on the order of the keys.
