Skip to lesson

learningjavascript.org / basics / 16-array-methods · lesson 6 of 25

TL;DR

JavaScript array methods tutorial — master map, filter, reduce, find, some, every, and more with interactive examples you can edit and run

Key concepts

  • JavaScript array methods
  • JS map filter reduce
  • array methods tutorial
  • JavaScript arrays

Array Methods

Arrays are one of the most common data structures in JavaScript, and the language ships with a powerful set of built-in methods for working with them. Instead of writing manual loops for every operation, array methods let you express intent clearly and concisely. In this lesson you will learn the most important ones and understand when to reach for each.

Transforming Data with map

Array.prototype.map creates a new array by applying a function to every element. The original array is never modified.

const products = [
  { name: "Keyboard", price: 79 },
  { name: "Mouse", price: 45 },
  { name: "Monitor", price: 320 },
];

// Add a discounted price to every product
const discounted = products.map((product) => ({
  ...product,
  salePrice: (product.price * 0.9).toFixed(2),
}));

console.log(discounted);
// Each object now has both price and salePrice

Notice that map always returns an array of the same length as the input. If you have 3 products going in, you get 3 products coming out — just transformed.

Filtering Data with filter

Array.prototype.filter creates a new array containing only the elements for which the callback returns a truthy value.

const scores = [42, 88, 61, 95, 73, 30, 100, 54];

const passing = scores.filter((score) => score >= 60);
const failing = scores.filter((score) => score < 60);

console.log("Passing scores:", passing);
console.log("Failing scores:", failing);
console.log("Pass rate:", `${Math.round((passing.length / scores.length) * 100)}%`);

You can chain map and filter together. For example, to get the names of expensive products:

const inventory = [
  { name: "USB Hub", price: 25, inStock: true },
  { name: "Webcam", price: 89, inStock: false },
  { name: "Headset", price: 149, inStock: true },
  { name: "Desk Lamp", price: 39, inStock: true },
  { name: "Chair Mat", price: 55, inStock: false },
];

const availableExpensive = inventory
  .filter((item) => item.inStock && item.price > 40)
  .map((item) => item.name);

console.log("Available items over $40:", availableExpensive);
// [ 'Headset' ]  — Desk Lamp ($39) fails the > 40 test; Webcam and Chair Mat are out of stock

Chaining works because each method returns a new array, so you can call the next method directly on the result.

Predict

Trace this pipeline by hand before running it. What does it log?

const products = [
{ name: "Pen", price: 3, inStock: true },
{ name: "Notebook", price: 12, inStock: false },
{ name: "Backpack", price: 60, inStock: true },
{ name: "Water Bottle", price: 18, inStock: true },
];

const result = products
.filter((p) => p.inStock)
.map((p) => p.price)
.filter((price) => price > 10);

console.log(result);
Continue learning

Reducing Arrays with reduce

Array.prototype.reduce is the most flexible array method. It collapses an array into a single value by accumulating results across each element.

The signature is: array.reduce(callback, initialValue). The callback receives the accumulator (running result) and the current element.

const orders = [
  { id: 1, total: 45.99 },
  { id: 2, total: 120.00 },
  { id: 3, total: 8.50 },
  { id: 4, total: 67.25 },
];

// Sum all order totals
const grandTotal = orders.reduce((sum, order) => sum + order.total, 0);
console.log("Grand total: $" + grandTotal.toFixed(2));

// Group orders by whether they are over $50
const grouped = orders.reduce(
  (groups, order) => {
    const key = order.total >= 50 ? "large" : "small";
    groups[key].push(order);
    return groups;
  },
  { large: [], small: [] }
);

console.log("Large orders:", grouped.large.length);
console.log("Small orders:", grouped.small.length);

Always provide an explicit initialValue (the second argument to reduce). Without it, reduce uses the first array element as the accumulator, which causes confusing bugs when working with arrays of objects.

Debug

This code is supposed to sum the prices and log 'Total: $444'. It doesn't throw — it prints something worse. Predict what it actually prints, then fix it so it logs 'Total: $444'.

const assert = require('assert');

const products = [
{ name: "Keyboard", price: 79 },
{ name: "Mouse", price: 45 },
{ name: "Monitor", price: 320 },
];

const total = products.reduce((sum, product) => sum + product.price);

assert.strictEqual(total, 444, 'expected 444, got ' + total);
console.log("Total: $" + total);

Expected output: Total: $444

Continue learning

Finding Elements

When you need a single element rather than a filtered list, use find and findIndex.

  • find returns the first matching element (or undefined if none match)
  • findIndex returns the index of the first matching element (or -1 if none match)
const users = [
  { id: 1, name: "Alice", role: "admin" },
  { id: 2, name: "Bob", role: "editor" },
  { id: 3, name: "Carol", role: "viewer" },
  { id: 4, name: "Dan", role: "editor" },
];

const admin = users.find((user) => user.role === "admin");
console.log("Admin:", admin.name);

const bobIndex = users.findIndex((user) => user.name === "Bob");
console.log("Bob is at index:", bobIndex);

// Safe lookup — find returns undefined if not found
const missing = users.find((user) => user.id === 99);
console.log("Missing user:", missing); // undefined

Testing Array Contents with some and every

Sometimes you do not need the elements themselves — you just need a yes/no answer about the collection.

  • some returns true if at least one element satisfies the condition
  • every returns true if all elements satisfy the condition
const cart = [
  { name: "Notebook", price: 12, available: true },
  { name: "Pen Set", price: 8, available: true },
  { name: "Stapler", price: 15, available: false },
];

const anyUnavailable = cart.some((item) => !item.available);
const allAffordable = cart.every((item) => item.price < 20);
const allAvailable = cart.every((item) => item.available);

console.log("Any unavailable?", anyUnavailable);   // true
console.log("All affordable?", allAffordable);     // true
console.log("All available?", allAvailable);       // false

if (anyUnavailable) {
  const unavailable = cart.filter((item) => !item.available).map((i) => i.name);
  console.log("Out of stock:", unavailable.join(", "));
}

Recall

Without scrolling up: you call three methods on the same array of objects — map, filter, and reduce (with an initial value of 0). map and filter each return one thing, reduce returns another. Which row correctly describes what each RETURNS?

Continue learning

Try It Yourself

Reading about array methods is not the same as reaching for them by intent. This is a build task: a small program that reports its own pass/fail. You are given a task list — an array of task objects — and three empty functions to finish. Run it and it fails immediately, telling you which function is still missing. Implement each one until every check passes and it prints All checks passed.

The three functions reuse exactly the methods this lesson taught: chaining filter into map to select and reshape, reduce with an explicit initial value to aggregate, and some to answer a yes/no question about the collection. The starter already has the data, the stubs, and the checks — you write only the logic inside each function. Nothing above spells out all three answers, so you will have to assemble them yourself.

Build

Finish the build. Three functions are stubbed out and the checks below them fail until each one returns the right value. Run it as-is to see which check fails first, decide what that function is missing, then implement the three functions until it prints 'All checks passed.' The checks run top to bottom, so the first failure you see is TODO 1 — implement it first, then work down.

const assert = require('assert');

// The data: a task list for the manager service. Do NOT change this array.
const tasks = [
{ title: "Write spec", status: "done", priority: 3 },
{ title: "Review PR", status: "pending", priority: 5 },
{ title: "Fix bug", status: "pending", priority: 8 },
{ title: "Deploy", status: "done", priority: 2 },
{ title: "Update docs", status: "pending", priority: 4 },
];

// TODO 1: return a NEW array of the titles of only the pending tasks, in order.
//   pendingTitles(tasks) -> ["Review PR", "Fix bug", "Update docs"]
function pendingTitles(tasks) {
// your code here
}

// TODO 2: return the sum of every task's priority as a number.
//   totalPriority(tasks) -> 22
function totalPriority(tasks) {
// your code here
}

// TODO 3: return true if ANY task has a priority of 8 or higher, else false.
//   hasUrgent(tasks) -> true
function hasUrgent(tasks) {
// your code here
}

// --- Build checks: these must all pass. Do not edit below this line. ---
assert.deepStrictEqual(
pendingTitles(tasks),
["Review PR", "Fix bug", "Update docs"],
"pendingTitles(tasks) should filter to pending tasks and map them to their titles, in order",
);
assert.strictEqual(
totalPriority(tasks),
22,
"totalPriority(tasks) should reduce every task's priority into a single sum (22)",
);
assert.strictEqual(
hasUrgent(tasks),
true,
"hasUrgent(tasks) should return true when at least one task has priority >= 8",
);

console.log("All checks passed.");
console.log("Pending titles:", pendingTitles(tasks));
console.log("Total priority:", totalPriority(tasks));
console.log("Any urgent?", hasUrgent(tasks));

Expected output: All checks passed. Pending titles: [ 'Review PR', 'Fix bug', 'Update docs' ] Total priority: 22 Any urgent? true

Continue learning

Once it passes, try two variations and predict each before running:

  1. Flip the filter predicate. In pendingTitles, change the filter test from status === "pending" to status === "done". Predict which titles come back, in order, before running. You now select the two done tasks and map them to ["Write spec", "Deploy"], so the check fails — proof that the filter predicate, not the map, decides which rows survive.
  2. some vs every. In hasUrgent, swap some for every, keeping the same priority >= 8 test. Decide what hasUrgent(tasks) returns before running. every demands all tasks clear the bar, and most tasks sit well under 8, so it returns false and the check fails — the difference between "at least one" and "all".

Capstone milestone

Milestone — the Task Manager service. The capstone's service (Step 3 of the capstone project) filters, sorts, and aggregates tasks with exactly these array methods. This lesson's exercises are that skill in miniature. Confirm you can reach for each method by intent.

Hint: You don't need the full capstone service yet — this confirms the map/filter/reduce fluency the service is built on. In the capstone, these same methods count pending tasks, filter by status, and build the stats line.

  • Used filter to select a subset of items by a condition
  • Used map to transform items into a new array without mutating the original
  • Used reduce with an explicit initial value to compute an aggregate (a sum, count, or grouping)
  • Used find (or some/every) to answer a single-item or yes/no question about the collection
Continue learning

Key Takeaways

  • map transforms every element and returns a new array of the same length — use it when you need to reshape data
  • filter returns a new array containing only elements that pass a test — the original array is never mutated
  • reduce is the Swiss Army knife: it can replicate map and filter, compute aggregates, and build complex data structures
  • find and findIndex stop at the first match, so they save exactly the part of the array they skip — a big win when the match is near the front, and no real win when it is near the end or missing, since then both they and filter scan everything
  • some and every return booleans and short-circuit early, so they are ideal for validation checks
  • All of these methods accept a callback function, making them composable and chainable
  • None of these methods mutate the original array (unlike push, pop, splice, or sort)

Pro Tip: Reach for map, filter, and reduce before writing a for loop. They signal intent clearly — a reader seeing filter immediately knows you are selecting a subset, whereas a for loop requires reading the body to understand what it does. When you do need a loop (for side effects like logging or DOM updates), forEach is the idiomatic choice.

Next Steps

Now that you can transform arrays with confidence, the next lesson covers destructuring and the spread operator — two features that make working with arrays and objects dramatically more concise.

Two-tier handoff: this document is the complete reading surface. Continue learning for stateful practice, progress, and real sandbox execution.