Warm-up · Activity 1 of 7
Warm-up: what does this print?
console.log([1, 2, 3].map((n) => n * 10));// B5.2 · ~30 min · Beginner
After this lesson you transform, select and total arrays with map, filter and reduce, chain them, and avoid their classic traps.
You will be able to
Warm-up · Activity 1 of 7
console.log([1, 2, 3].map((n) => n * 10));Predict · Activity 2 of 7
const nums = [5, 12, 8, 20];
console.log(nums.filter((n) => n > 10), nums);Practice · Activity 3 of 7
console.log([2, 3, 4].reduce((sum, n) => sum + n, ____));Practice · Activity 4 of 7
Practice · Activity 5 of 7
console.log(["a", "b", "c"].map((letter, i) => letter + i));Brain teaser · Activity 6 of 7
console.log(["1", "2", "3"].map(parseInt));Apply · Activity 7 of 7
Check your work against this list
Read the worked example, then write the exercises. Your code runs in your browser or on your computer and is never uploaded.
Worked example
Five days of step counts, worked on with the methods of this lesson: map turns them into thousands and into labels, filter keeps the days that reached 10,000 steps, reduce totals the week, some and every answer yes/no questions, and the last part chains filter into reduce. The final line shows that steps itself never changed.
main.js
// Step counts for one working week: transform, select, total.
const steps = [4200, 11800, 7600, 12500, 9100];
const inThousands = steps.map((n) => Math.round(n / 1000));
console.log(inThousands);
const labels = steps.map((n, i) => `Day ${i + 1}: ${n}`);
console.log(labels[0], "|", labels.at(-1));
const goalDays = steps.filter((n) => n >= 10000);
console.log(goalDays, goalDays.length);
const total = steps.reduce((sum, n) => sum + n, 0);
console.log(total, total / steps.length);
console.log(steps.some((n) => n < 5000), steps.every((n) => n > 5000));
const goalTotal = steps.filter((n) => n >= 10000).reduce((sum, n) => sum + n, 0);
console.log(goalTotal, steps);
Run it with
node main.jsOutput
[ 4, 12, 8, 13, 9 ]
Day 1: 4200 | Day 5: 9100
[ 11800, 12500 ] 2
45200 9040
true false
24300 [ 4200, 11800, 7600, 12500, 9100 ]Tab indents and Shift+Tab outdents. To leave the editor with the keyboard, press Esc, then Tab.
The first run downloads the JavaScript runner (up to 0.1 MB) and keeps it cached. Your code runs in your browser’s own engine and stays on your device.
Exercise 1 of 2
squares(nums) should return a new array with every number squared, and passed(scores) a new array with only the scores of 50 or more. The starter picks the wrong method in both functions: one returns undefined, the other an array of true and false. Fix each with the right method. Keep the word export in front of each function.
Tab indents and Shift+Tab outdents. To leave the editor with the keyboard, press Esc, then Tab.
The first run downloads the JavaScript runner (up to 0.1 MB) and keeps it cached. Your code runs in your browser’s own engine and stays on your device.
Run the tests: squares returns undefined. What does forEach always return?
Use map when you want one new item per old item: nums.map((n) => n * n).
passed returns [true, false, …] because map keeps whatever the callback returns. Which method keeps the items for which the callback is true?
One way to solve it. Yours can look different and still pass the checks.
// Return a new array with every number squared.
export function squares(nums) {
return nums.map((n) => n * n);
}
// Return a new array with only the scores of 50 or more.
export function passed(scores) {
return scores.filter((s) => s >= 50);
}
Install ECMAScript 2026 or newer. Save these files in one folder, open a terminal in that folder, and run the commands below.
main.js
// Return a new array with every number squared.
export function squares(nums) {
return nums.forEach((n) => n * n);
}
// Return a new array with only the scores of 50 or more.
export function passed(scores) {
return scores.map((s) => s >= 50);
}
main.test.js
import {test} from 'node:test';
import assert from 'node:assert/strict';
import {squares, passed} from './main.js';
test('squares([1, 2, 3]) returns [1, 4, 9]', () => {
const got = squares([1, 2, 3]);
assert.deepEqual(got, [1, 4, 9], `squares([1, 2, 3]) returned ${JSON.stringify(got)}, expected [1,4,9]`);
});
test('squares leaves the array it was given unchanged', () => {
const nums = [4, 5];
squares(nums);
assert.deepEqual(nums, [4, 5], `after the call the array was ${JSON.stringify(nums)}, expected [4,5]`);
});
test('passed keeps only the scores of 50 or more', () => {
const got = passed([72, 45, 50, 38]);
assert.deepEqual(got, [72, 50], `passed([72, 45, 50, 38]) returned ${JSON.stringify(got)}, expected [72,50]`);
});
test('passed returns an empty array when nobody passed', () => {
const got = passed([10, 20]);
assert.deepEqual(got, [], `passed([10, 20]) returned ${JSON.stringify(got)}, expected []`);
});
package.json
{
"type": "module"
}
package.json tells Node.js that the .js files are modules; keep it in the folder.
Run the program:
node main.jsRun the checks (needs learnrun.js in the same folder):
node --testDownload learnrun.jsExercise 2 of 2
bigOrdersTotal(orders, limit) should add up the orders of limit or more, and return 0 when there are none. anyRefund(orders) should return true when at least one order is negative. The starter totals correctly when some orders are big enough, but stops with a TypeError when none are, and anyRefund asks the wrong question. Fix both.
Tab indents and Shift+Tab outdents. To leave the editor with the keyboard, press Esc, then Tab.
The first run downloads the JavaScript runner (up to 0.1 MB) and keeps it cached. Your code runs in your browser’s own engine and stays on your device.
Run the tests: with [5, 3] the filter keeps nothing. What does reduce do with an empty array and no initial value?
Give reduce an initial value as its second argument: reduce((sum, o) => sum + o, 0).
every asks "are all orders negative?". Which method asks "is at least one negative?"
One way to solve it. Yours can look different and still pass the checks.
// Add up the orders of limit or more; 0 when there are none.
export function bigOrdersTotal(orders, limit) {
return orders.filter((o) => o >= limit).reduce((sum, o) => sum + o, 0);
}
// true when at least one order is negative.
export function anyRefund(orders) {
return orders.some((o) => o < 0);
}
Install ECMAScript 2026 or newer. Save these files in one folder, open a terminal in that folder, and run the commands below.
main.js
// Add up the orders of limit or more; 0 when there are none.
export function bigOrdersTotal(orders, limit) {
return orders.filter((o) => o >= limit).reduce((sum, o) => sum + o);
}
// true when at least one order is negative.
export function anyRefund(orders) {
return orders.every((o) => o < 0);
}
main.test.js
import {test} from 'node:test';
import assert from 'node:assert/strict';
import {bigOrdersTotal, anyRefund} from './main.js';
test('Adds up only the orders of 10 or more', () => {
const got = bigOrdersTotal([20, 5, 50, 10], 10);
assert.equal(got, 80, `bigOrdersTotal([20, 5, 50, 10], 10) returned ${got}, expected 80`);
});
test('Returns 0 when no order is big enough', () => {
const got = bigOrdersTotal([5, 3], 10);
assert.equal(got, 0, `bigOrdersTotal([5, 3], 10) returned ${got}, expected 0`);
});
test('anyRefund finds one negative order among others', () => {
const got = anyRefund([12, -4, 30]);
assert.equal(got, true, `anyRefund([12, -4, 30]) returned ${got}, expected true`);
});
test('anyRefund is false when no order is negative', () => {
const got = anyRefund([12, 30]);
assert.equal(got, false, `anyRefund([12, 30]) returned ${got}, expected false`);
});
package.json
{
"type": "module"
}
package.json tells Node.js that the .js files are modules; keep it in the folder.
Run the program:
node main.jsRun the checks (needs learnrun.js in the same folder):
node --testDownload learnrun.jsconst prices = [];
const total = prices.reduce((sum, p) => sum + p);
console.log(total);
What Node.js prints
TypeError: Reduce of empty array with no initial valueWhy, and the fix
Without an initial value, reduce uses the first item as the starting sum, and an empty array has no first item. The same happens after a filter that keeps nothing. Pass a starting value as the second argument, 0 for a sum: prices.reduce((sum, p) => sum + p, 0) returns 0 for an empty array.
const nums = [1.5, 2.25];
const doubled = nums.map((n) => {
n * 2;
});
console.log(doubled[0].toFixed(1));
What Node.js prints
TypeError: Cannot read properties of undefined (reading 'toFixed')Why, and the fix
An arrow function with curly braces returns only what a return statement gives it, as lesson B4.4 showed. Here the callback computes n * 2 and throws it away, so map builds [ undefined, undefined ]. Write return n * 2; inside the braces, or drop the braces: nums.map((n) => n * 2).
const names = ["ada", "linus"];
const upper = names.forEach((n) => n.toUpperCase());
console.log(upper.join(", "));
What Node.js prints
TypeError: Cannot read properties of undefined (reading 'join')Why, and the fix
forEach runs the callback for each item but always returns undefined, so upper is undefined and has no join method. To get a new array of results, use map: names.map((n) => n.toUpperCase()). Keep forEach for side effects, such as printing each item.
JavaScript in the browser: your browser’s own engine, in a sandboxed worker. Syntax errors are located with acorn 8.18.0, MIT. Licence and source
5 questions, no hints. Score 80% or more to complete the lesson.
Finish every activity above to unlock the exit ticket.