JavaScript / ES6+ Bootcamp Lesson 4 – Arrays, map, filter, find, reduce, Spread and Rest

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 3
value: 10 20 30 40

Access:

numbers[0]; // 10
numbers[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:

1
2
3

Conceptually:

1 → callback(1)
2 → callback(2)
3 → callback(3)

The important point:

forEach is 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]
callback
number => number * 2
output
[2, 4, 6]

The key idea:

map takes 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 data
map
↓
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 array
filter → returns an array
find → 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
↓
0
0 + 10 = 10
10 + 20 = 30
30 + 30 = 60

Final result:

60

14. Understand the two arguments to reduce

This:

numbers.reduce((total, number) => {
return total + number;
}, 0);

contains:

total
↓
accumulator
number
↓
current element
0
↓
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 = 0
number = 10
0 + 10 = 10

Accumulator becomes:

10

Second iteration

sum = 10
number = 20
10 + 20 = 30

Third iteration

sum = 30
number = 30
30 + 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:

MethodPurposeReturns
forEachPerform actionundefined
mapTransform every itemNew array
filterKeep matching itemsNew array
findFind first matchOne item / undefined
someAny match?Boolean
everyAll match?Boolean
reduceAccumulate/build resultAnything

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: John
age: 30
age: 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 → expand
rest → 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 item
rest → 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, Mike
names
// ["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 values
rest → 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.