Once you are comfortable with loops, most of the ones you write turn out to be the same four shapes:
- Change every item into something else. Prices to prices with tax.
- Keep some items. Orders that are unpaid.
- Reduce many items to one value. A total, a count, a maximum.
- Find one item. The user with this id.
JavaScript has a method for each, and using them says what you meant rather than how you did it.
```
// The loop
const names = [];
for (const user of users) {
names.push(user.name);
}
// The transformation
const names = users.map((user) => user.name);
```
The second version is shorter, but the real gain is that map announces "same number of items, each changed". A reader knows that before reading the body.
The four
| Shape | Method | Returns |
|---|---|---|
| Change each | map | A new array, same length |
| Keep some | filter | A new array, shorter or equal |
| Combine to one | reduce | Any single value |
| Find one | find | The item, or undefined |
Plus two that answer yes or no: some (is any true?) and every (are all true?).
They do not change the original
map and filter return new arrays and leave the input alone. That matters more than it sounds: functions that do not modify their inputs are much easier to reason about, because calling one cannot break something elsewhere.
```
const prices = [10, 20];
const withTax = prices.map((p) => p * 1.2);
// prices is still [10, 20]
```
Chaining
Because each returns an array, they compose:
```
const total = orders
.filter((order) => !order.refunded)
.map((order) => order.amount)
.reduce((sum, amount) => sum + amount, 0);
```
That reads as a sentence: drop the refunds, take the amounts, add them up.