JavaScript Arrays

Arrays are the workhorse of JavaScript data handling. Learn how to create, manipulate, and transform arrays using map, filter, reduce, and the full suite of modern array methods.

Beginner 12 min read 13 examples

Array Basics

Arrays are ordered lists of values. JavaScript arrays are dynamic (no fixed size), can hold mixed types, and are zero-indexed.

JavaScript
// Array literal (preferred)
const fruits = ["apple", "banana", "cherry"];
const mixed  = [1, "two", true, null, { id: 5 }];
const empty  = [];

// Access by index (zero-based)
console.log(fruits[0]);        // "apple"
console.log(fruits[2]);        // "cherry"
console.log(fruits.at(-1));    // "cherry" (last element - ES2022)
console.log(fruits.at(-2));    // "banana"

// Length
console.log(fruits.length);    // 3
console.log(fruits[fruits.length - 1]); // "cherry" (pre-ES2022 way)

// Check if value is an array
console.log(Array.isArray(fruits)); // true
console.log(Array.isArray("hi"));   // false

// Create arrays from other things
const from1 = Array.from("hello");   // ["h","e","l","l","o"]
const from2 = Array.from({length: 3}, (_, i) => i); // [0, 1, 2]
const from3 = Array.of(7);           // [7] (not [empty x 7])

Add and Remove Elements

These methods modify the original array (mutable operations):

JavaScript
const arr = ["a", "b", "c"];

// push / pop (end of array)
arr.push("d");           // add to end
console.log(arr);        // ["a","b","c","d"]
const last = arr.pop();  // remove from end, returns removed
console.log(last);       // "d"
console.log(arr);        // ["a","b","c"]

// unshift / shift (start of array)
arr.unshift("z");        // add to start
console.log(arr);        // ["z","a","b","c"]
const first = arr.shift(); // remove from start
console.log(first);      // "z"
console.log(arr);        // ["a","b","c"]

// splice(start, deleteCount, ...items)
arr.splice(1, 1);        // remove 1 element at index 1
console.log(arr);        // ["a","c"]

arr.splice(1, 0, "b");   // insert "b" at index 1 (delete 0)
console.log(arr);        // ["a","b","c"]

arr.splice(1, 1, "X");   // replace element at index 1
console.log(arr);        // ["a","X","c"]

Iterating Arrays

JavaScript
const nums = [10, 20, 30, 40];

// for...of - cleanest for simple iteration
for (const num of nums) {
    console.log(num);  // 10, 20, 30, 40
}

// forEach - for side effects, no return value
nums.forEach((num, index) => {
    console.log(`${index}: ${num}`);
});
// 0: 10, 1: 20, 2: 30, 3: 40

// entries() - iterate with index
for (const [index, value] of nums.entries()) {
    console.log(index, value);
}

// Classic for loop - use when you need break/continue or index
for (let i = 0; i < nums.length; i++) {
    if (nums[i] === 30) break;
    console.log(nums[i]);  // 10, 20
}
forEach vs for...of

Both iterate arrays. Prefer for...of when you need to break or continue - you cannot break out of forEach. Use forEach when you are performing side effects and want the index/value API. For transformations (producing a new value), use map/filter/reduce instead of either.

map and filter

map() transforms every element and returns a new array of the same length. filter() keeps only elements that pass a test, returning a shorter (or equal) array.

JavaScript
const numbers = [1, 2, 3, 4, 5, 6, 7, 8];

// map - transform each element
const doubled = numbers.map(n => n * 2);
console.log(doubled); // [2, 4, 6, 8, 10, 12, 14, 16]

const squared = numbers.map(n => n ** 2);
console.log(squared); // [1, 4, 9, 16, 25, 36, 49, 64]

// filter - keep elements that pass the test
const evens = numbers.filter(n => n % 2 === 0);
console.log(evens); // [2, 4, 6, 8]

const odds = numbers.filter(n => n % 2 !== 0);
console.log(odds);  // [1, 3, 5, 7]

// Chain map and filter
const result = numbers
    .filter(n => n > 3)       // [4, 5, 6, 7, 8]
    .map(n => n * 10);        // [40, 50, 60, 70, 80]
console.log(result);

// Practical: transform an array of objects
const users = [
    { name: "Alice", age: 28, active: true },
    { name: "Bob",   age: 17, active: false },
    { name: "Carol", age: 32, active: true },
];

const activeAdults = users
    .filter(u => u.active && u.age >= 18)
    .map(u => u.name);
console.log(activeAdults); // ["Alice", "Carol"]

reduce

reduce() iterates the array and accumulates a single result. It is the most powerful (and most flexible) array method.

JavaScript
// Syntax: array.reduce((accumulator, current) => ..., initialValue)

const nums = [1, 2, 3, 4, 5];

// Sum
const sum = nums.reduce((acc, n) => acc + n, 0);
console.log(sum); // 15

// Product
const product = nums.reduce((acc, n) => acc * n, 1);
console.log(product); // 120

// Maximum value
const max = nums.reduce((acc, n) => (n > acc ? n : acc), -Infinity);
console.log(max); // 5

// Count occurrences
const words = ["apple", "banana", "apple", "cherry", "banana", "apple"];
const counts = words.reduce((acc, word) => {
    acc[word] = (acc[word] ?? 0) + 1;
    return acc;
}, {});
console.log(counts); // { apple: 3, banana: 2, cherry: 1 }

// Group by property
const people = [
    { name: "Alice", dept: "Engineering" },
    { name: "Bob",   dept: "Design" },
    { name: "Carol", dept: "Engineering" },
];
const byDept = people.reduce((acc, person) => {
    (acc[person.dept] ??= []).push(person.name);
    return acc;
}, {});
console.log(byDept);
// { Engineering: ["Alice","Carol"], Design: ["Bob"] }

Methods for testing and searching arrays:

JavaScript
const users = [
    { id: 1, name: "Alice", age: 28 },
    { id: 2, name: "Bob",   age: 17 },
    { id: 3, name: "Carol", age: 32 },
];

// find - returns first matching element (or undefined)
const bob = users.find(u => u.name === "Bob");
console.log(bob); // { id: 2, name: "Bob", age: 17 }

const notFound = users.find(u => u.age > 100);
console.log(notFound); // undefined

// findIndex - returns index of first match (or -1)
const idx = users.findIndex(u => u.id === 3);
console.log(idx); // 2

// some - true if ANY element passes the test
console.log(users.some(u => u.age < 18));  // true (Bob is 17)
console.log(users.some(u => u.age > 100)); // false

// every - true if ALL elements pass the test
console.log(users.every(u => u.age >= 18)); // false (Bob is 17)
console.log(users.every(u => u.name.length > 2)); // true

// includes - check if a primitive value is in the array
const nums = [1, 2, 3, NaN];
console.log(nums.includes(2));   // true
console.log(nums.includes(NaN)); // true (unlike indexOf, works with NaN)
console.log(nums.indexOf(NaN));  // -1 (indexOf fails with NaN)

Sorting Arrays

sort() sorts the array in-place and returns it. Always pass a comparator function when sorting numbers.

JavaScript
// String sort (default - works correctly for strings)
const fruits = ["banana", "apple", "cherry", "date"];
fruits.sort();
console.log(fruits); // ["apple","banana","cherry","date"]

// Number sort - MUST use comparator
const nums = [10, 2, 100, 21, 3];
nums.sort();              // WRONG: [10, 100, 2, 21, 3] (lexicographic)
nums.sort((a, b) => a - b); // ascending
console.log(nums);        // [2, 3, 10, 21, 100]
nums.sort((a, b) => b - a); // descending
console.log(nums);        // [100, 21, 10, 3, 2]

// Sort objects by property
const users = [
    { name: "Carol", age: 32 },
    { name: "Alice", age: 28 },
    { name: "Bob",   age: 17 },
];
users.sort((a, b) => a.age - b.age);          // by age ascending
users.sort((a, b) => a.name.localeCompare(b.name)); // alphabetical

// Non-mutating sort (ES2023)
const sorted = nums.toSorted((a, b) => a - b); // returns new array
console.log(nums);   // original unchanged
console.log(sorted); // sorted copy

flat, spread, and slice

JavaScript
// flat - flatten nested arrays
const nested = [1, [2, 3], [4, [5, 6]]];
console.log(nested.flat());     // [1, 2, 3, 4, [5, 6]] (one level)
console.log(nested.flat(2));    // [1, 2, 3, 4, 5, 6]   (two levels)
console.log(nested.flat(Infinity)); // flatten all levels

// flatMap - map then flat(1) in one step
const sentences = ["hello world", "foo bar"];
const words = sentences.flatMap(s => s.split(" "));
console.log(words); // ["hello","world","foo","bar"]

// Spread operator - copy or merge arrays
const a = [1, 2, 3];
const b = [4, 5, 6];
const merged = [...a, ...b];        // [1,2,3,4,5,6]
const copy   = [...a];              // shallow copy

// Remove duplicates
const dup = [1, 2, 2, 3, 3, 4];
const unique = [...new Set(dup)];
console.log(unique); // [1, 2, 3, 4]

// slice - non-mutating subarray
const nums = [10, 20, 30, 40, 50];
console.log(nums.slice(1, 3));   // [20, 30]
console.log(nums.slice(-2));     // [40, 50]
console.log(nums.slice());       // [10,20,30,40,50] (shallow copy)

Quick Reference

MethodMutates?ReturnsUse for
push/popYesnew length / removedAdd/remove at end
unshift/shiftYesnew length / removedAdd/remove at start
spliceYesremoved elementsInsert/remove anywhere
sortYessorted arraySort in-place
reverseYesreversed arrayReverse in-place
mapNonew arrayTransform elements
filterNonew arrayKeep matching elements
reduceNosingle valueAggregate to one value
findNoelement or undefinedFirst match
some/everyNobooleanTest conditions
sliceNonew arrayExtract subarray
flat/flatMapNonew arrayFlatten nested arrays
includesNobooleanCheck if value exists

Frequently Asked Questions

map() returns a new array of transformed values - use it when you need the results. forEach() returns undefined and is used purely for side effects (logging, DOM updates, etc.). In modern code, prefer map/filter/reduce for data transformation since they are composable and do not mutate the original array.

The cleanest way: [...new Set(array)]. A Set only stores unique values, and the spread operator converts it back to an array. For objects (where uniqueness is by reference, not value), you need a more complex approach using filter with indexOf or a Map keyed by a unique property.

slice(start, end) extracts a portion of the array and returns it as a new array without changing the original. splice(start, deleteCount, ...items) modifies the original array - it can remove, replace, or insert elements. Remember: slice = read-only copy, splice = mutates original.

Never use array.sort() alone on numbers - it converts to strings first: [10, 9, 2].sort() gives [10, 2, 9] (wrong!). Always pass a comparator: array.sort((a, b) => a - b) for ascending, array.sort((a, b) => b - a) for descending. For strings, sort() without a comparator works correctly.