In the last lesson we learned how to loop over arrays. That's great, but JavaScript gives you even more powerful tools for working with arrays. In this lesson we'll cover the spread operator, the rest parameter, and three array methods you'll use constantly: map, filter, and reduce.
Spread
The spread operator looks like three dots: .... When you put it in front of an array, it "spreads out" each element individually. This is useful when you want to copy an array or combine arrays without changing the originals.
const cities = ["Islamabad", "Lahore", "Karachi"];
// copy an array
const moreCities = [...cities];
console.log(moreCities);
console.log(moreCities === cities); // false — it's a new array
// combine arrays
const northernCities = ["Peshawar", "Muzaffarabad"];
const allCities = [...northernCities, ...cities];
console.log(allCities);
// add items when copying
const withNewCity = ["Quetta", ...cities, "Multan"];
console.log(withNewCity);
Spread is also handy when you want to pass the items of an array as separate arguments to a function:
const numbers = [3, 7, 2, 9];
console.log(Math.max(...numbers)); // 9
Without spread you'd have to write Math.max(numbers[0], numbers[1], numbers[2], numbers[3]). Spread does that for you.
Rest
Rest uses the same ... syntax, but it does the opposite of spread: instead of spreading items out, it collects them into an array. You'll most often see rest in function parameters.
function sum(first, ...rest) {
console.log(first); // the first argument, by itself
console.log(rest); // everything else, collected into an array
}
sum(10, 20, 30, 40);
// first: 10
// rest: [20, 30, 40]
Here first gets the first value passed in, and ...rest gathers every remaining argument into an array called rest. The name rest is just a convention, you can call it anything, but people usually call it rest or args.
Let's use rest to build something useful:
function sum(...numbers) {
let total = 0;
for (const num of numbers) {
total += num;
}
return total;
}
console.log(sum(1, 2, 3)); // 6
console.log(sum(10, 20, 30, 40)); // 100
Same dots, different job: spread expands an array into individual items; rest collects individual items into an array. The difference is where the ... appears, spread goes on an array you're reading from; rest goes on a parameter you're writing.
map
map runs a function on every item in an array and gives you back a new array with the results. The original array is not changed.
const prices = [500, 1200, 800, 2500];
const pricesWithTax = prices.map(function (price) {
return price * 1.17;
});
console.log(prices); // [500, 1200, 800, 2500] — unchanged
console.log(pricesWithTax); // [585, 1404, 936, 2925]
You can also use an arrow function, which you'll see a lot with map:
const cities = ["islamabad", "lahore", "karachi"];
const capitalized = cities.map((city) => {
return city[0].toUpperCase() + city.slice(1);
});
console.log(capitalized); // ["Islamabad", "Lahore", "Karachi"]
Think of map as "transform every item." You always get back an array with the same number of items, just changed in some way.
filter
filter runs a function on every item and keeps only the ones where your function returns true. Like map, it returns a new array and does not change the original.
const scores = [45, 72, 38, 91, 55, 88, 33];
const passingScores = scores.filter(function (score) {
return score >= 50;
});
console.log(passingScores); // [72, 91, 55, 88]
Another example with objects, very common in real projects:
const courses = [
{ teacher: "Fiaz Hussain", course: "Full Stack Web Development", seats: 30 },
{ teacher: "Sara Khan", course: "Mobile App Development", seats: 0 },
{ teacher: "Ahmed Raza", course: "Data Analysis with Python", seats: 12 },
];
const availableCourses = courses.filter((course) => {
return course.seats > 0;
});
console.log(availableCourses);
Think of filter as "keep only the items that pass this test."
reduce
reduce is the most flexible of the three. It loops over an array and accumulates a single result, a number, a string, an object, or even another array. You pass it a function and a starting value.
const prices = [500, 1200, 800, 2500];
const total = prices.reduce(function (runningTotal, price) {
return runningTotal + price;
}, 0);
console.log(total); // 5000
The function receives two arguments on each loop:
runningTotal: the value built up so far (starts at0because that's the second argument toreduce)price: the current item from the array
Each time through, you return the new running total. After the last item, reduce gives you the final result.
Here's a different use, counting how many items match a condition:
const cities = ["Islamabad", "Lahore", "Karachi", "Lahore", "Peshawar"];
const cityCount = cities.reduce(function (counts, city) {
if (counts[city]) {
counts[city]++;
} else {
counts[city] = 1;
}
return counts;
}, {});
console.log(cityCount);
// { Islamabad: 1, Lahore: 2, Karachi: 1, Peshawar: 1 }
Don't worry if that last example feels heavy reduce takes practice. The key idea is: start with an initial value, update it on each loop, return the final value.
Putting it together
These tools often show up in the same code. Say you have a list of product prices and you want the total of only the expensive ones:
const products = [
{ name: "Keyboard", price: 3500 },
{ name: "Mouse", price: 1200 },
{ name: "Monitor", price: 28000 },
{ name: "Webcam", price: 4500 },
{ name: "USB Cable", price: 500 },
];
const expensiveTotal = products
.filter((product) => product.price >= 3000)
.map((product) => product.price)
.reduce((total, price) => total + price, 0);
console.log(expensiveTotal); // 36000
Read this chain from left to right:
filter: keep only products costing 3000 or moremap: pull out just the price from each onereduce: add them all up
None of the original data is modified. Each step returns a new value for the next step.
A quick recap
| Tool | What it does |
|---|---|
...spread |
Expands an array into individual items |
...rest |
Collects remaining arguments into an array |
.map() |
Transform every item → new array (same length) |
.filter() |
Keep items that pass a test → new array (shorter or same length) |
.reduce() |
Accumulate everything → one final value |
You won't memorize all of this immediately, and that's fine. The more you use them, the more natural they'll feel. When you see ... in code, ask yourself: is this spreading something out, or collecting things together? When you see .map, .filter, or .reduce, read the chain step by step, each one is just a loop with a specific job.