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 at 0 because that's the second argument to reduce)
  • 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:

  1. filter: keep only products costing 3000 or more
  2. map: pull out just the price from each one
  3. reduce: 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.