This lesson is extremely important for React.
A large amount of React code looks like this:
users.map(...)
users.filter(...)
users.find(...)
items.reduce(...)
Once you understand what these methods are doing with callbacks, React code becomes much easier to read.
The goal today is not to memorize methods. It is to understand the pattern:
array
โ
array method
โ
callback
โ
new result
1. JavaScript arrays
Start with:
const numbers = [10, 20, 30, 40];
An array has:
index: 0 1 2 3value: 10 20 30 40
Access:
numbers[0]; // 10numbers[2]; // 30
Length:
numbers.length; // 4
Unlike some languages, JavaScript arrays can contain different types:
const values = [10, "John", true, null];
But in normal application code, arrays usually contain consistent types:
const users = [ { id: 1, name: "John" }, { id: 2, name: "Jane" }];
2. forEach – execute something for every item
Let’s start with the simplest callback method.
const numbers = [1, 2, 3];numbers.forEach(number => { console.log(number);});
Output:
123
Conceptually:
1 โ callback(1)2 โ callback(2)3 โ callback(3)
The important point:
forEachis mainly for performing an action for each item.
For example:
users.forEach(user => {
console.log(user.name);
});
It does not create a transformed array for you.
3. map – transform every item
This is one of the most important methods for React.
const numbers = [1, 2, 3];
const doubled = numbers.map(number => number * 2);
Result:
[2, 4, 6]
Think:
input[1, 2, 3]callbacknumber => number * 2output[2, 4, 6]
The key idea:
maptakes every element, runs your callback, and builds a new array from the returned values.
4. What does map actually do?
Conceptually, this:
const doubled = numbers.map(number => number * 2);
behaves roughly like:
const doubled = [];
doubled.push(1 * 2);
doubled.push(2 * 2);
doubled.push(3 * 2);
Result:
[2, 4, 6]
The real implementation is more sophisticated, but this is the right mental model.
5. map with objects
This is where React developers use it constantly.
const users = [ { id: 1, name: "John" }, { id: 2, name: "Jane" }, { id: 3, name: "Mike" }];
Extract names:
const names = users.map(user => user.name);
Result:
["John", "Jane", "Mike"]
The callback:
user => user.name
is called once for each object.
6. map can transform objects
Suppose we want to create a different structure:
const users = [ { id: 1, name: "John", age: 30 }, { id: 2, name: "Jane", age: 25 }];const result = users.map(user => ({ id: user.id, displayName: user.name}));
Result:
[ { id: 1, displayName: "John" }, { id: 2, displayName: "Jane" }]
Notice the parentheses:
user => ({ id: user.id, displayName: user.name})
We need them because the arrow function is implicitly returning an object.
7. filter – keep matching elements
Now:
const numbers = [1, 2, 3, 4, 5];const result = numbers.filter(number => number > 2);
Result:
[3, 4, 5]
Unlike map, the callback for filter answers:
Should this item be included?
The callback must produce something truthy or falsy.
Conceptually:
1 โ false โ remove
2 โ false โ remove
3 โ true โ keep
4 โ true โ keep
5 โ true โ keep
8. filter with users
const users = [ { id: 1, name: "John", active: true }, { id: 2, name: "Jane", active: false }, { id: 3, name: "Mike", active: true }];const activeUsers = users.filter(user => user.active);
Result:
[ { id: 1, name: "John", active: true }, { id: 3, name: "Mike", active: true }]
Very common React pattern:
activeUsers.map(user => ( <div key={user.id}> {user.name} </div>))
This combines two concepts:
filter โchoose datamap โtransform/render data
9. find – find one item
find looks for the first matching item.
const numbers = [10, 20, 30, 40];const result = numbers.find(number => number > 20);
Result:
30
Notice something important:
map โ returns an arrayfilter โ returns an arrayfind โ returns one item
10. find with objects
const users = [ { id: 1, name: "John" }, { id: 2, name: "Jane" }];const user = users.find(user => user.id === 2);
Result:
{ id: 2, name: "Jane" }
If nothing matches:
const user = users.find(user => user.id === 99);console.log(user);
Result:
undefined
This is important when handling lookup failures.
11. some
Another useful method:
const numbers = [1, 2, 3, 4];numbers.some(number => number > 3);
Result:
true
It asks:
Does at least one item satisfy this condition?
For example:
const hasAdmin = users.some(user => user.role === "admin");
12. every
every asks:
Do all items satisfy the condition?
const numbers = [2, 4, 6];numbers.every(number => number % 2 === 0);
Result:
true
13. reduce – the most important one to understand carefully
reduce often looks scary:
const result = numbers.reduce((total, number) => { return total + number;}, 0);
Don’t memorize this.
Understand what it is doing.
Given:
const numbers = [10, 20, 30];
we want:
10 + 20 + 30
reduce maintains an accumulated value.
Think:
initial accumulator โ 00 + 10 = 1010 + 20 = 3030 + 30 = 60
Final result:
60
14. Understand the two arguments to reduce
This:
numbers.reduce((total, number) => { return total + number;}, 0);
contains:
totalโaccumulatornumberโcurrent element0โinitial value
So:
(total, number) => total + number
means:
Take the accumulated result and the current item, then produce the next accumulated result.
15. reduce step by step
Given:
const numbers = [10, 20, 30];const total = numbers.reduce( (sum, number) => sum + number, 0);
Execution:
First iteration
sum = 0number = 100 + 10 = 10
Accumulator becomes:
10
Second iteration
sum = 10number = 2010 + 20 = 30
Third iteration
sum = 30number = 3030 + 30 = 60
Final:
60
16. reduce can create objects
This is where reduce becomes powerful.
Suppose:
const users = [ { id: 1, name: "John" }, { id: 2, name: "Jane" }, { id: 3, name: "Mike" }];
We want:
{ 1: "John", 2: "Jane", 3: "Mike"}
We can do:
const result = users.reduce((acc, user) => { acc[user.id] = user.name; return acc;}, {});
Result:
{ 1: "John", 2: "Jane", 3: "Mike"}
Don’t worry if reduce still feels less intuitive. That’s normal.
We’ll practice it.
17. map vs filter vs find vs reduce
Keep this table in your head:
| Method | Purpose | Returns |
|---|---|---|
forEach | Perform action | undefined |
map | Transform every item | New array |
filter | Keep matching items | New array |
find | Find first match | One item / undefined |
some | Any match? | Boolean |
every | All match? | Boolean |
reduce | Accumulate/build result | Anything |
This is much more useful than memorizing code examples.
18. Chain array methods
You can combine methods.
const users = [ { name: "John", active: true }, { name: "Jane", active: false }, { name: "Mike", active: true }];const names = users .filter(user => user.active) .map(user => user.name);
Step 1:
filter(user => user.active)
produces:
[ { name: "John", active: true }, { name: "Mike", active: true }]
Then:
map(user => user.name)
produces:
["John", "Mike"]
This style is extremely common in React.
19. Spread syntax
Now let’s move to another extremely important ES6 feature.
Suppose:
const numbers = [1, 2, 3];
We can copy its elements into a new array:
const newNumbers = [...numbers];
Result:
[1, 2, 3]
The ... here is called spread syntax.
Think:
...numbers โtake elements out โ1, 2, 3
20. Add an item with spread
const users = ["John", "Jane"];const newUsers = [...users, "Mike"];
Result:
["John", "Jane", "Mike"]
This is extremely important in React.
Instead of mutating:
users.push("Mike");
you often create a new array:
setUsers([...users, "Mike"]);
Why?
Because React state updates rely heavily on creating new references rather than mutating existing state.
We’ll discuss the React-specific reasoning later.
21. Spread with objects
Given:
const user = { name: "John", age: 30};
Create a new object:
const updatedUser = { ...user, age: 31};
Result:
{ name: "John", age: 31}
The later age: 31 overrides the previous value.
Think:
...user โname: Johnage: 30age: 31 โoverwrite age
22. Spread is heavily used in React
For example:
setUser({ ...user, name: "Jane"});
Suppose:
user = { id: 1, name: "John", age: 30};
The result becomes:
{ id: 1, name: "Jane", age: 30}
We didn’t modify the original object.
We created a new object.
23. Rest syntax
Here’s where JavaScript can get confusing.
The same ... syntax can also mean rest.
Compare:
Spread
const numbers = [1, 2, 3];const copy = [...numbers];
We’re taking values out.
Rest
function sum(...numbers) { console.log(numbers);}
Now we’re collecting values into an array.
sum(10, 20, 30);
Inside the function:
numbers = [10, 20, 30]
So:
spread โ expandrest โ collect
Same ..., different context.
24. Rest parameters
Example:
function sum(...numbers) { return numbers.reduce((total, number) => { return total + number; }, 0);}
Now:
sum(10, 20, 30);
Inside:
numbers// [10, 20, 30]
Result:
60
This is a beautiful example because it combines:
rest+reduce+callback
25. Destructuring + rest
You can also use rest during destructuring.
const numbers = [10, 20, 30, 40];const [first, ...rest] = numbers;
Now:
first;// 10
and:
rest;// [20, 30, 40]
This means:
first โ first itemrest โ everything remaining
26. Object destructuring + rest
Very common in React:
const user = { id: 1, name: "John", age: 30};const { id, ...details } = user;
Now:
id// 1
and:
details// { name: "John", age: 30 }
You will see patterns like this in React props.
27. A React-style example
Consider:
function User({ id, ...userDetails }) { return ( <div> <h2>{userDetails.name}</h2> <p>{userDetails.email}</p> </div> );}
The props object might be:
{ id: 10, name: "John", email: "john@example.com"}
After:
const { id, ...userDetails } = props;
you get:
id = 10;userDetails = { name: "John", email: "john@example.com"};
This syntax becomes much easier once destructuring and rest are clear.
28. A complete example
Let’s combine everything.
const users = [ { id: 1, name: "John", active: true, age: 30 }, { id: 2, name: "Jane", active: false, age: 25 }, { id: 3, name: "Mike", active: true, age: 35 }];
Find active users
const activeUsers = users.filter(user => user.active);
Get their names
const names = activeUsers.map(user => user.name);
Get total age
const totalAge = activeUsers.reduce( (sum, user) => sum + user.age, 0);
Result:
activeUsers// John, Mikenames// ["John", "Mike"]totalAge// 65
The whole pipeline:
users โ โโโ filter โโโ active users โ โโโ map โโโโโโ names โ โโโ reduce โโโ total age
๐ Ruby comparison
These should look familiar:
Ruby:
users.select { |user| user[:active] }
JavaScript:
users.filter(user => user.active);
Ruby:
users.map { |user| user[:name] }
JavaScript:
users.map(user => user.name);
Ruby:
users.find { |user| user[:id] == 2 }
JavaScript:
users.find(user => user.id === 2);
Ruby:
users.reduce(0) { |sum, user| sum + user[:age] }
JavaScript:
users.reduce((sum, user) => sum + user.age, 0);
The collection-processing concepts are familiar. The biggest thing to master is JavaScript’s function syntax and callback model.
โ ๏ธ One important distinction: map does NOT modify the original array
const numbers = [1, 2, 3];const doubled = numbers.map(n => n * 2);console.log(numbers);console.log(doubled);
Output:
[1, 2, 3][2, 4, 6]
The original remains unchanged.
Similarly:
const filtered = numbers.filter(n => n > 1);
creates another array.
This is one reason these methods fit well with React’s preference for immutable state updates.
๐ฏ Int. questions
Try answering these yourself.
Q1
What’s the difference between:
forEach()map()filter()find()reduce()
Q2
What does this return?
[1, 2, 3, 4].map(n => n * 2);
Q3
What does this return?
[1, 2, 3, 4].filter(n => n % 2 === 0);
Q4
What does this return?
[10, 20, 30].find(n => n > 15);
Q5
What is the final value?
[10, 20, 30].reduce( (sum, n) => sum + n, 0);
Q6
What is the difference between spread and rest?
const copy = [...numbers];
versus:
function test(...numbers) {}
๐งช Live coding practice
Try solving these without using a loop first.
Problem 1 – Double numbers
doubleNumbers([1, 2, 3, 4]);// [2, 4, 6, 8]
Use map.
Problem 2 – Active users
getActiveUsers(users);
Return only users where:
user.active === true
Use filter.
Problem 3 – Find user
findUser(users, 2);
Return the user whose id is 2.
Use find.
Problem 4 – Total
sum([10, 20, 30, 40]);// 100
Use reduce.
Problem 5 – Method chaining
Given:
const users = [ { name: "John", active: true }, { name: "Jane", active: false }, { name: "Mike", active: true }, { name: "Sarah", active: false }];
Produce:
["John", "Mike"]
using:
filter + map
Problem 6 – Spread
Given:
const users = ["John", "Jane"];
Create:
["John", "Jane", "Mike"]
without using push.
Problem 7 – Rest
Write:
sum(10, 20, 30, 40);
and make it return:
100
using a rest parameter.
Mental model for today
Remember these seven statements:
forEach โ do something for every item
map โ transform every item
filter โ keep matching items
find โ get the first matching item
some โ does at least one match?
every โ do all match?
reduce โ accumulate/build one result
And:
spread โ expand valuesrest โ collect values
The most important connection from today’s lesson is:
Array method โtakes a callback โcallback runs for each relevant item โmethod produces a result
For example:
users .filter(user => user.active) .map(user => user.name);
You should now be able to read that almost as English:
“Take users, keep the active ones, then transform each remaining user into their name.”
Next lesson: Objects, destructuring, spread, this, and JavaScript’s object model. This will connect directly to React props and explain why const { name } = user and const [users, setUsers] = useState([]) look similar but work differently.