This article is produced with scandiweb's eCommerce expertise

Collaborate with our development, PPC, SEO, data & analytics, or customer experience teams to grow your eCommerce business.

JavaScript Array Methods Explained (With Examples)

You have an array of cart line items, and you need three things from it at once: a running order total, a filtered view of the products still in stock, and a clean sorted list to hand to the UI. You can reach for a for loop and a few temporary variables, and it will work. It will also bury what you actually meant under a pile of index bookkeeping that the next developer has to decode.

Array methods are how you say what you mean. map, filter, and reduce describe the transformation instead of the mechanics, and the newer ES2023 methods finally let you reorder and patch arrays without mutating the original. This guide walks through the methods you reach for daily on a real storefront, with the gotchas that cause silent bugs, and the modern additions that most older tutorials never mention.

At scandiweb, arrays run through almost everything we build: search results, cart contents, product attributes, breadcrumb trails, media galleries. The examples below come from that kind of work, not from a textbook.

Overview

  • Transformation methods (map, filter, reduce) return a new array or value and leave the original untouched, which keeps data flow easy to follow.
  • Search and iteration methods (forEach, find, some, every, includes) answer questions about an array without rebuilding it.
  • Modern ES2023 copying methods (toSorted, toReversed, toSpliced, with) do the job of the old mutating methods without changing the source array, which matters in React and any state-driven UI.

πŸš€ Quick takeaway

The fastest way to write array bugs is to mutate an array another part of your app is still reading. Default to methods that return a new array, and reach for the mutating ones only when you know nothing else holds a reference.

JavaScript array methods cheat sheet grouped by what each method returns
Array prototype cheat sheet

What are array methods in JavaScript?

Array methods are built-in functions on Array.prototype that operate on the contents of an array. Most take a callback function that runs against each element, and they fall into a few groups: methods that transform an array into a new one, methods that reduce it to a single value, methods that search it, and methods that reorder or patch it.

The distinction that matters most in day-to-day code is mutation. Some methods change the array you call them on (sort, reverse, push, splice). Others leave it alone and return something new (map, filter, slice, and the ES2023 copying methods). In a UI framework that compares references to decide what to re-render, mutating shared state is a recipe for components that quietly fail to update.

πŸš€ Quick takeaway

Before you pick a method, ask whether it mutates. map, filter, slice and the to-prefixed methods return a new array. sort, reverse, push and splice change the one you already have. In shared state, that difference is the bug or the fix.

Creating a new array by transforming each item

Array.prototype.map() calls a function for each value and returns an array of the results.

Return value: a new array where each element is whatever the callback returned.

import React from "react";
import "./styles.css";

const names = ["whale", "squid", "turtle", "coral", "starfish"];

export default class App extends React.PureComponent {
  // Declare the mapper function once
  renderMarineLifeItem = (name) => <p key={name}>{name}</p>;

  // Use the pre-declared mapper function here
  renderMarineLifeList = () => (
    <div>{names.map(this.renderMarineLifeItem)}</div>
  );

  render() {
    return (
      <div className="App">
        <h1>MarineLife Sample</h1>
        {this.renderMarineLifeList()}
      </div>
    );
  }
}

Using map to render a list is one of the most common patterns in React. There is a trap hiding in it, though: avoid declaring arrow functions inline in the render path.

// Avoid this
<ul>{names.map(name => <li key={name}>{name}</li>)}</ul>

The reason is the render cycle. A sample from the web usually runs a handful of times, so nobody notices. Render a list of a thousand items, and you have declared a thousand redundant functions. Declaring the mapper once, as in the first example, sidesteps that.

Keep in mind:

  1. The callback you pass to map needs an explicit return, or map hands back an array full of undefined.
  2. If you forget, map will not complain. It returns the array of nothing quietly, and silent bugs like that are the slow kind to track down.

MDN: Array.prototype.map()

Reducing an array to a single value

Array.prototype.reduce() combines all elements into one new value.

Return value: the single value that results from the reduction.

reduce earns its keep in state management (this is the shape behind Redux reducers) and any time the output is a different size or type than the input. Two terms travel with it: the accumulator is the value you are building up, and the reducer is the function that folds each item into it.

const superheroes = [
  { name: 'batman', type: 'fighting' },
  { name: 'superman', type: 'flying' },
  { name: 'aquaman', type: 'swimming' }
];

const getMapFromArray = (data) =>
  data.reduce((acc, item) => {
    // add object key to our object i.e. batman: { type: 'fighting' }
    acc[item.name] = { type: item.type };
    return acc;
  }, {});

getMapFromArray(superheroes);

Here reduce turns an array into a keyed object, which is a faster lookup structure when you need to find items by id.

Keep in mind:

  1. Include a return statement and pass the initial value (the second argument to reduce).
  2. Do not expect an array back. reduce returns one value, whatever shape you build.

MDN: Array.prototype.reduce()

πŸš€ Quick takeaway

If you can describe an operation as “fold this list down into one thing,” it is a reduce, even when that one thing is an object or a Map rather than a number.

Keeping only the items you want

Array.prototype.filter() returns a new array with only the items that pass a test.

Return value: a new array of the elements that returned true. If none pass, you get an empty array, not null.

A common storefront case is stripping out null and undefined before they break downstream code.

const attributes = {
  size:     { attribute_label: 'Size',  code: 1 },
  color:    { attribute_label: 'Color', code: 2 },
  material: { attribute_label: undefined, code: 3 },
  texture:  { attribute_label: null,      code: 4 }
};

const labels = Object.values(attributes)
  .filter((attribute) => attribute && attribute.attribute_label) // drop null and undefined
  .map(({ attribute_label }) => attribute_label);

// ["Size", "Color"]

Keep in mind:

  1. The callback must return a boolean. With a concise arrow function the return is implicit, otherwise include it.
  2. Forget the return and the callback yields undefined, which filter coerces to false, so you get a silent empty array.
  3. Return a non-boolean and filter applies JavaScript‘s type-coercion rules to guess your intent. That is usually a bug, and usually a silent one.

MDN: Array.prototype.filter()

Searching an array: find, some, every, and includes

Not every question needs a new array. Sometimes you just want an answer, and these methods give you one without rebuilding anything.

  • find() returns the first element that matches a test, or undefined. Use it to pull one record out of a list.
  • findIndex() returns the position of that first match, or -1.
  • some() returns true if at least one element passes the test. Good for “is anything in the cart out of stock?”
  • every() returns true only if all elements pass. Good for “are all required fields filled?”
  • includes() returns true if the array contains a given value, using a straight equality check rather than a callback.
const cart = [
  { sku: 'A-1', qty: 2, inStock: true },
  { sku: 'B-7', qty: 1, inStock: false }
];

cart.find(line => line.sku === 'B-7');      // { sku: 'B-7', qty: 1, inStock: false }
cart.some(line => !line.inStock);           // true, show a backorder notice
cart.every(line => line.qty > 0);           // true, safe to enable checkout
['A-1', 'B-7'].includes('B-7');             // true

πŸš€ Quick takeaway

Reach for find and some before filter when you only need one result or a yes/no answer. filter walks the whole array and builds a new one even when the first match would have done.

MDN: Array.prototype.find()

Looping for side effects: forEach

Array.prototype.forEach() runs a function for each element and returns undefined.

forEach is the method to use when you want a side effect rather than a new array: logging, firing an analytics event per item, writing to something outside the array.

cart.forEach((line) => {
  trackEvent('cart_line_viewed', { sku: line.sku, qty: line.qty });
});

The thing to remember: forEach returns nothing, so you cannot chain off it, and you cannot stop it early. If you need to break out partway, use a for...of loop or some (which stops at the first true).

MDN: Array.prototype.forEach()

Reordering an array with sort

Array.prototype.sort() sorts the array according to a comparison function, in place.

Return value: the sorted array. The catch is “in place” – sort mutates the original.

// An unsorted breadcrumbs array
const breadcrumbs = [
  { category_name: "Home",          category_level: 1 },
  { category_name: "Personal Care", category_level: 3 },
  { category_name: "Health & Beauty", category_level: 2 }
];

// Sort a copy, ascending by category_level
const sortedBreadcrumbs = [...breadcrumbs].sort(
  (a, b) => a.category_level - b.category_level
);

Note the [...breadcrumbs]. Because sort mutates, the spread syntax makes a copy first so the original order survives. You do not always need to clone, but in a component reading from shared state you almost always do.

Keep in mind:

  1. sort changes the original array as well as returning it, which is not ideal from a functional standpoint. Clone first if anything else reads the source.
  2. By default sort compares elements as strings, so numbers sort alphabetically (10 before 2). Pass a comparison function for numeric order.
  3. Default string sorting ignores Unicode and non-English alphabets. For locale-aware order use String.prototype.localeCompare():
const strings = ['č', 'é', 'A', 'b', 'Đ'];
const localeSort = [...strings].sort((a, b) =>
  a.localeCompare(b, 'en', { sensitivity: 'base' })
);

// ['A', 'b', 'č', 'Đ', 'é']

MDN: Array.prototype.sort()

πŸš€ Quick takeaway

sort and reverse change the array in place. If a component, a selector, or anything else still reads that array, clone it first or use toSorted and toReversed, which hand back a new array and leave the source alone.

Flattening nested arrays: flat and flatMap

Product data arrives nested more often than you would like: variants inside products, tags inside categories. Two methods handle that.

  • flat(depth) returns a new array with sub-array elements pulled up one level by default, or as deep as you ask.
  • flatMap(callback) runs map and then flattens one level, in a single pass. It is the right tool when each item expands into zero, one, or many results.
const orders = [
  { id: 1, items: ['A-1', 'A-2'] },
  { id: 2, items: ['B-7'] },
  { id: 3, items: [] }
];

// Every SKU across all orders, in one flat list
const allSkus = orders.flatMap(order => order.items);
// ['A-1', 'A-2', 'B-7']

Because an empty items array contributes nothing, flatMap also doubles as a “map and drop” when you return [] for items you want to skip.

MDN: Array.prototype.flatMap()

πŸš€ Quick takeaway

flatMap is map plus a one-level flat in a single pass. Returning an empty array from the callback removes an item, returning two values adds two, which makes it a tidy expand-and-filter in one step.

Reading from the end: at() and findLast()

Two newer additions make working with the end of an array readable.

  • at(index) (ES2022) reads an element by index and accepts negative numbers, so arr.at(-1) is the last element without the arr[arr.length - 1] dance.
  • findLast() and findLastIndex() (ES2023) are find and findIndex searching from the end, which is what you want for “the most recent order that shipped.”
const prices = [12, 40, 8, 25];
prices.at(-1);                       // 25 (last element)

const orders = [
  { id: 1, status: 'shipped' },
  { id: 2, status: 'pending' },
  { id: 3, status: 'shipped' }
];
orders.findLast(o => o.status === 'shipped'); // { id: 3, status: 'shipped' }

MDN: Array.prototype.at()

The modern copying methods: toSorted, toReversed, toSpliced, and with

This is the part most older tutorials predate. ES2023 added four methods that do the work of the mutating classics without touching the original array, which removes the [...spread] ceremony and a whole category of state bugs.

  • toSorted(compareFn) is sort that returns a new sorted array.
  • toReversed() is reverse that returns a new reversed array.
  • toSpliced(start, deleteCount, ...items) is splice that returns a new array with the change applied.
  • with(index, value) returns a new array with a single index replaced.
const prices = [12, 40, 8, 25];

const sorted   = prices.toSorted((a, b) => a - b); // [8, 12, 25, 40]
const reversed = prices.toReversed();              // [25, 8, 40, 12]
const patched  = prices.with(0, 99);               // [99, 40, 8, 25]

prices; // [12, 40, 8, 25], untouched

These are the methods to reach for in React state updates. Instead of cloning and then mutating the clone, you call one method and hand the result straight to setState. They are part of ES2023 and are supported across current browsers and Node 20 and newer (MDN), so for any project on an evergreen toolchain they are safe to use today.

πŸš€ Quick takeaway

If you are still writing const sorted = […arr].sort(fn), the ES2023 method arr.toSorted(fn) says the same thing in fewer characters and removes any chance of mutating the source by accident.

Adding and removing items without mutation

For the everyday “add one” and “remove one” cases, you rarely need a method at all.

// Add to the front
const array = [1, 2, 3];
const withNew = [42, ...array];          // [42, 1, 2, 3]

// Remove by value
const cleaned = [42, 1, 2, 3].filter(item => item !== 42); // [1, 2, 3]

// Replace one index (ES2023)
const replaced = [1, 2, 3].with(1, 99);  // [1, 99, 3]

Spread for adding, filter for removing, and with for replacing all return new arrays, which keeps the original safe for anything else still reading it.

How to choose the right array method

When you are unsure which method fits, the question to ask is what you want back.

  • A new array the same length, each item transformed: map.
  • A shorter array, some items dropped: filter.
  • A single value or object folded from many: reduce.
  • One matching item or its index: find / findIndex (or findLast from the end).
  • A yes/no answer: some (any) or every (all), or includes for a plain value check.
  • A reordered or patched array without mutation: toSorted, toReversed, toSpliced, with.
  • A side effect per item, nothing returned: forEach.

Matching the method to the return value is most of what separates code that reads clearly from code that hides its intent. If you build storefronts on a React or PWA frontend, this habit pays off every time another developer opens the file. It is the same discipline we apply across Magento React development work, and it starts with the basics covered in our website development primer.

Frequently asked questions

What is the difference between map and forEach?

map returns a new array of the callback’s results and is for transforming data. forEach returns undefined and is for side effects like logging or analytics. If you want a new array, use map. If you want to do something per item and keep nothing, use forEach.

Does sort mutate the original array?

Yes. Array.prototype.sort() sorts in place and returns the same array. To sort without changing the original, copy first with [...arr].sort(fn) or use the ES2023 arr.toSorted(fn), which returns a new sorted array.

What is the difference between find and filter?

find returns the first single element that passes the test, or undefined. filter returns a new array of every element that passes. Use find when one result is enough, since it stops at the first match instead of scanning the whole array.

When should I use reduce instead of map or filter?

Use reduce when the output is a different shape or size than the input, for example folding an array of objects into a single total, a keyed lookup object, or a Map. If you are producing another array of the same length, map is clearer.

Are the ES2023 array methods safe to use in production?

toSorted, toReversed, toSpliced, with, findLast, and findLastIndex are part of ES2023 and are supported in all current browsers and Node 20 and later. On an evergreen toolchain they are safe. If you must support very old runtimes, check your targets or include a polyfill.

How do I get the last element of an array?

Use array.at(-1). The at() method accepts negative indexes counting from the end, so it reads cleanly without array[array.length - 1].

Clean array code is not the point – the storefront it powers is. If your team is wrestling a React or PWA frontend into shape and the data layer keeps fighting back, our frontend development team can help you get it under control. Tell us what you are building.

If you enjoyed this post, you may also like