JavaScript Arrays — The Complete Course


Prerequisites

Basic JavaScript: variables, functions, if, and loops.


Level 1 — Basics

Array Literals

Arrays store ordered collections. Created with square brackets:

const fruits = ['apple', 'banana', 'cherry'];
const mixed = [1, 'hello', true, null, { id: 5 }];
const empty = [];

Element Access

Zero-indexed. First element at [0], last at [length - 1].

const colors = ['red', 'green', 'blue'];
colors[0];   // 'red'
colors[1];   // 'green'
colors[2];   // 'blue'
colors[99];  // undefined (no error)

Element Assignment

const arr = ['a', 'b', 'c'];
arr[1] = 'x';
// arr: ['a', 'x', 'c']

You can assign to any index. If the index is beyond length, the array grows and empty slots fill the gap:

const arr = ['a'];
arr[3] = 'b';
// arr: ['a', empty × 2, 'b']
// arr.length: 4

The length Property

length is not the count of elements — it’s the highest index + 1.

const arr = ['a', 'b', 'c'];
arr.length; // 3

// Truncate
arr.length = 1;
// arr: ['a']

// Extend (creates empty slots)
arr.length = 5;
// arr: ['a', empty × 4]

Checking for Arrays

Array.isArray([1, 2, 3]);     // true
Array.isArray('hello');        // false
Array.isArray({ length: 3 });  // false (array-like ≠ array)

Quiz: Element Assignment

What does arr look like after this code?

const arr = [10, 20];
arr[5] = 50;
Answer

arr is [10, 20, empty × 3, 50] with length === 6.

Quiz: Element Access

What does this return?

[1, 2, 3][10]
Answer

undefined. Accessing a non-existent index never throws — it returns undefined.

Quiz: Empty Array

What is the length of []? What is [][0]?

Answer

length is 0. [][0] is undefined.


Level 2 — Stack, Iteration & Map

Stack Operations: push and pop

Arrays work as stacks. push appends, pop removes from the end.

const stack = [];
stack.push('a');           // ['a']
stack.push('b', 'c');      // ['a', 'b', 'c']  — push returns new length: 3
stack.pop();               // returns 'c', stack: ['a', 'b']
stack.pop();               // returns 'b', stack: ['a']
stack.pop();               // returns 'a', stack: []
stack.pop();               // returns undefined, stack: [] (no error on empty)

Key detail: push returns the new length. pop returns the removed element (or undefined if empty).

Queue Operations: shift and unshift

const q = ['b', 'c'];
q.unshift('a');            // ['a', 'b', 'c'] — returns new length: 3
q.shift();                 // returns 'a', q: ['b', 'c']
q.shift();                 // returns 'b', q: ['c']
q.shift();                 // returns 'c', q: []
q.shift();                 // returns undefined, q: [] (empty)

shift/unshift are O(n) — they re-index all remaining elements. Prefer push/pop for performance.

forEach — Side Effects

Executes a callback for each element. Returns undefined. Use for side effects (logging, mutating external state), not for building new arrays.

const fruits = ['apple', 'banana', 'cherry'];
fruits.forEach((fruit, index, array) => {
  console.log(`${index}: ${fruit}`);
});
// 0: apple
// 1: banana
// 2: cherry

Callback signature: (element, index, array). index and array are optional.

Key behavior: forEach skips empty slots in sparse arrays:

const sparse = ['a', , 'c'];
sparse.forEach((v, i) => console.log(i, v));
// 0 'a'
// 2 'c'
// Index 1 is skipped entirely

Cannot break out of a forEach early. Use for...of, some, every, or find instead.

map — Transformation

Creates a new array where each element is the return value of the callback. Never mutates the original.

const nums = [1, 2, 3];
const doubled = nums.map(n => n * 2);
// doubled: [2, 4, 6]
// nums unchanged: [1, 2, 3]

Map with index:

const indexed = ['a', 'b', 'c'].map((item, i) => ({ index: i, value: item }));
// [{ index: 0, value: 'a' }, { index: 1, value: 'b' }, { index: 2, value: 'c' }]

Extract a field (pluck):

const users = [
  { id: 1, name: 'Alice' },
  { id: 2, name: 'Bob' },
];
const names = users.map(u => u.name);
// ['Alice', 'Bob']

Map skips empty slots (like forEach):

[, 2, , 4].map(x => x * 2);
// [empty, 4, empty, 8]

Quiz: Add Exclamation

const words = ['hello', 'world'];
// Use map to produce: ['hello!', 'world!']
Answer
words.map(w => w + '!');

Quiz: Square

const nums = [1, 2, 3, 4, 5];
// Use map to produce: [1, 4, 9, 16, 25]
Answer
nums.map(n => n * n);

flat and flatMap

flat(depth) — flattens nested arrays to the specified depth (default 1):

const nested = [1, [2, [3, [4]]]];
nested.flat();            // [1, 2, [3, [4]]]
nested.flat(2);           // [1, 2, 3, [4]]
nested.flat(Infinity);    // [1, 2, 3, 4] — completely flatten

flatMap(callback)map then flat(1) in one pass. More efficient than separate calls.

const sentences = ['Hello world', 'foo bar'];
sentences.flatMap(s => s.split(' '));
// ['Hello', 'world', 'foo', 'bar']

// Expand variants
const products = [
  { name: 'shirt', sizes: ['S', 'M', 'L'] },
  { name: 'hat', sizes: ['One Size'] },
];
products.flatMap(p => p.sizes);
// ['S', 'M', 'L', 'One Size']

Why flatMap instead of map + flat? It only flattens one level and avoids creating the intermediate mapped array.


Level 3 — Combining, Slicing & Joining

concat — Merge Arrays

Returns a new array combining the original with other arrays or values. Non-mutating.

const a = [1, 2];
const b = [3, 4];
a.concat(b);                // [1, 2, 3, 4]
a.concat(b, [5, 6]);        // [1, 2, 3, 4, 5, 6]
a.concat(7, 8);             // [1, 2, 7, 8]
// a unchanged: [1, 2]

Spread syntax often reads better:

const combined = [...a, ...b];  // [1, 2, 3, 4]

slice(start, end) — Extract a Shallow Copy

Returns a new array containing elements from start (inclusive) to end (exclusive). Non-mutating.

const arr = ['a', 'b', 'c', 'd', 'e'];
arr.slice(0, 2);    // ['a', 'b']
arr.slice(2, 4);    // ['c', 'd']
arr.slice(1);       // ['b', 'c', 'd', 'e'] — end defaults to length
arr.slice();        // ['a', 'b', 'c', 'd', 'e'] — shallow copy

slice() with no arguments is the idiomatic way to shallow-copy an array:

const copy = arr.slice();
// Same as: [...arr]

Slice With Negative Arguments

Negative indices count from the end. -1 is the last element.

const arr = ['a', 'b', 'c', 'd'];
arr.slice(-2);      // ['c', 'd'] — last 2
arr.slice(-3, -1);  // ['b', 'c']
arr.slice(1, -1);   // ['b', 'c'] — from index 1 to the second-to-last

join(separator) — Array to String

Joins all elements into a string with the separator (default ',').

['a', 'b', 'c'].join();         // 'a,b,c'
['a', 'b', 'c'].join('');       // 'abc'
['a', 'b', 'c'].join(' - ');    // 'a - b - c'
[1, 2, null, 3].join(',');      // '1,2,,3' — null/undefined become empty string
[].join(',');                   // ''

Quiz: Copy an Array

Give three different ways to create a shallow copy of const arr = [1, 2, 3].

Answer
const copy1 = arr.slice();
const copy2 = [...arr];
const copy3 = Array.from(arr);

Quiz: Get First N Elements

const data = [10, 20, 30, 40, 50];
// Get first 3 elements
Answer
data.slice(0, 3); // [10, 20, 30]

Level 4 — Searching & Reference Behavior

Arrays Are Objects

Arrays are objects. The indices are string keys:

const arr = ['a', 'b'];
arr[0];        // 'a'
arr['0'];      // 'a' — same thing
arr[0] === arr['0'];  // true

You can set arbitrary properties (though don’t):

arr.customProp = 'hello';
arr.length;         // still 2 — custom props don't affect length

The real implication: arrays have reference semantics:

const a = [1, 2, 3];
const b = a;
b.push(4);
// a is now [1, 2, 3, 4] — same object!

To truly copy: const b = [...a] or b = a.slice().

indexOf(value) / lastIndexOf(value)

Finds the first/last index of a value using strict equality (===). Returns -1 if not found.

const arr = [1, 2, 3, 2, 1];
arr.indexOf(2);         // 1
arr.lastIndexOf(2);     // 3
arr.indexOf(99);        // -1
arr.indexOf(NaN);       // -1 — NaN !== NaN

Use findIndex for objects (see Level 5).

includes(value)

Returns boolean. Uses === like indexOf, but returns true/false instead of an index.

[1, 2, 3].includes(2);        // true
[1, 2, 3].includes(99);       // false

Important distinction:

const obj = { id: 1 };
[obj].includes(obj);            // true — same reference
[{ id: 1 }].includes({ id: 1 }); // false — different objects

includes finds NaN (unlike indexOf):

[NaN].includes(NaN);    // true
[NaN].indexOf(NaN);     // -1

Copying Arrays — Shallow vs Deep

All built-in copy methods create shallow copies:

const original = [{ a: 1 }, { b: 2 }];
const shallow = [...original];
shallow[0].a = 99;
// original[0].a is now 99 — nested objects are shared

Deep copy options:

const deep1 = JSON.parse(JSON.stringify(original));
const deep2 = structuredClone(original);  // better: handles Dates, Map, Set, etc.

shift() / unshift() — Front Operations

Revisit from Level 2 with more detail:

const arr = ['a', 'b', 'c'];
arr.shift();    // returns 'a', arr: ['b', 'c']
arr.unshift('x'); // returns 3, arr: ['x', 'b', 'c']

shift and unshift re-index all elements. For large arrays, consider a different data structure or reverse + push/pop.

findIndex(callback) — Find Index by Condition

const users = [
  { id: 1, name: 'Alice' },
  { id: 2, name: 'Bob' },
];
const idx = users.findIndex(u => u.id === 2);
// 1

Replace an item immutably:

const updated = [...users];
const i = updated.findIndex(u => u.id === 2);
if (i !== -1) updated[i] = { ...updated[i], name: 'Robert' };

Level 5 — Finding, Filling & Negative Indexes

find(callback) — Find First Matching Element

Returns the first element where callback returns truthy. Returns undefined if nothing matches. Short-circuits — stops at first match.

const users = [
  { id: 1, name: 'Alice' },
  { id: 2, name: 'Bob' },
  { id: 3, name: 'Alice' },
];
users.find(u => u.name === 'Alice');
// { id: 1, name: 'Alice' } — only the FIRST match
users.find(u => u.name === 'Charlie');
// undefined

findLast(callback) / findLastIndex(callback) (ES2023)

Search from the end:

const logs = [
  { level: 'info', msg: 'start' },
  { level: 'error', msg: 'first error' },
  { level: 'info', msg: 'retry' },
  { level: 'error', msg: 'last error' },
];
logs.findLast(l => l.level === 'error');
// { level: 'error', msg: 'last error' }
logs.findLastIndex(l => l.level === 'error');
// 3

Quiz: Implement uniq

Write a function uniq(arr) that returns a new array with duplicates removed.

Solution
const uniq = arr => [...new Set(arr)];
// Or without Set:
const uniq = arr => arr.filter((v, i, a) => a.indexOf(v) === i);

new Array(n) and fill

new Array(n) creates an array of n empty slots:

const arr = new Array(3);
// arr: [empty × 3]
arr.length; // 3
// WARNING: no elements, not even undefined

fill(value, start?, end?) — fills elements with a static value. Mutates.

new Array(3).fill(0);
// [0, 0, 0]

const arr = [1, 2, 3, 4, 5];
arr.fill(0, 1, 4);
// arr: [1, 0, 0, 0, 5]

Create a range:

[...new Array(5)].map((_, i) => i);
// [0, 1, 2, 3, 4]

// Better:
Array.from({ length: 5 }, (_, i) => i);
// [0, 1, 2, 3, 4]

Gotcha: new Array(3).map((_, i) => i) returns [empty × 3] because map skips empty slots. Always .fill() first or use Array.from.

Negative Array Indexes

Standard bracket notation does not support negative indexes — arr[-1] returns undefined.

Use at(index) (ES2022) for negative indexing:

const arr = [10, 20, 30, 40];
arr.at(0);    // 10
arr.at(-1);   // 40 — last element
arr.at(-2);   // 30
arr.at(99);   // undefined (no error)

Before at, the idiom was arr[arr.length - 1].

Quiz: Implement find

Write myFind(arr, callback) that works like Array.prototype.find.

Solution
const myFind = (arr, callback) => {
  for (let i = 0; i < arr.length; i++) {
    if (callback(arr[i], i, arr)) return arr[i];
  }
  return undefined;
};

Quiz: Rotate Right

Write a function that rotates an array one position to the right.

rotateRight([1, 2, 3, 4]);
// [4, 1, 2, 3]
Solution
const rotateRight = arr => [arr.at(-1), ...arr.slice(0, -1)];

Level 6 — Filtering, Sparse Arrays & Testing

Empty Slots (Sparse Arrays)

Empty slots occur when:

const a = [1, , 3];           // literal with hole
const b = new Array(5);       // uninitialized
const c = [1, 2];
c[5] = 3;                     // c: [1, 2, empty × 3, 3]

Critical difference: Empty slots are not the same as undefined:

const sparse = [1, , 3];
const dense = [1, undefined, 3];

// forEach skips empty slots
sparse.forEach(v => console.log(v)); // 1, 3
dense.forEach(v => console.log(v));  // 1, undefined, 3

// map skips empty slots
sparse.map(v => v);  // [1, empty, 3]
dense.map(v => v);   // [1, undefined, 3]

// find treats empty slots as undefined
sparse.find(v => v === undefined); // undefined — wait, it stops at index 1? No, find visits all indices and treats empty as undefined

Let me clarify: older methods (forEach, map, filter, reduce, every, some) skip empty slots. Newer methods (find, findIndex, includes, keys, entries, fill, join, toReversed, toSorted) treat empty slots as undefined.

// Older methods skip:
[, 1].forEach(v => console.log(v));
// 1

// Newer methods include:
[, 1].find(v => v === undefined);
// undefined (visits index 0, gets undefined)

[, 1].includes(undefined);
// true

filter(callback) — Keep Matching Elements

Returns a new array with only elements where the callback returns truthy. Non-mutating.

const nums = [1, 2, 3, 4, 5, 6];
nums.filter(n => n % 2 === 0);
// [2, 4, 6]

Common patterns:

const activeUsers = users.filter(u => u.active);
const admins = users.filter(u => u.role === 'admin');

// Remove falsy values (compact)
[0, '', null, undefined, false, 1, 'hello'].filter(Boolean);
// [1, 'hello']

// Filter + map chain
products
  .filter(p => p.price > 100)
  .map(p => p.name);

Quiz: Fill Dynamically

Create an array [0, 0, 0, 0, 0] of length 5 without writing each zero manually.

Answer
new Array(5).fill(0);

some(callback) and every(callback)

some — returns true if any element matches. Short-circuits on first truthy return.

const nums = [1, 2, 3, 4, 5];
nums.some(n => n > 3);   // true (4 is > 3)
nums.some(n => n > 10);  // false

every — returns true if all elements match. Short-circuits on first falsy return.

nums.every(n => n > 0);   // true
nums.every(n => n < 5);   // false (5 is not < 5)

Edge cases:

[].some(() => {});         // false — no elements to test
[].every(() => {});        // true — vacuous truth

Quiz: Even Numbers

Use filter to get even numbers from [1, 2, 3, 4, 5, 6].

Answer
[1, 2, 3, 4, 5, 6].filter(n => n % 2 === 0);
// [2, 4, 6]

Quiz: Implement none

Write none(arr, callback) that returns true if no element matches.

Solution
const none = (arr, callback) => !arr.some(callback);
// Or:
const none = (arr, callback) => arr.every(v => !callback(v));

Quiz: Implement filter with forEach

Implement filter using forEach.

Solution
const myFilter = (arr, callback) => {
  const result = [];
  arr.forEach((el, i, a) => {
    if (callback(el, i, a)) result.push(el);
  });
  return result;
};

Level 7 — Sorting, Reducing & Reversing

Quiz: Implement compact

Write a function that removes all falsy values from an array.

Solution
const compact = arr => arr.filter(Boolean);
compact([0, 1, false, 2, '', 3, null, undefined, NaN]);
// [1, 2, 3]

sort(compareFunction) — ⚠️ Mutates

sort() without a compare function converts elements to strings and sorts lexicographically:

[1, 10, 2, 20].sort();
// [1, 10, 2, 20] — WRONG for numbers!

Always provide a compare function. The function returns:

  • negative → a before b
  • positive → b before a
  • zero → unchanged order (not guaranteed across engines)
// Numbers ascending
[3, 1, 4, 1, 5].sort((a, b) => a - b);
// [1, 1, 3, 4, 5]

// Numbers descending
[3, 1, 4, 1, 5].sort((a, b) => b - a);
// [5, 4, 3, 1, 1]

// Strings (locale-aware)
names.sort((a, b) => a.localeCompare(b));

// Objects by field
people.sort((a, b) => a.age - b.age);

// Multiple fields
people.sort((a, b) =>
  a.lastName.localeCompare(b.lastName) ||
  a.firstName.localeCompare(b.firstName)
);

sort mutates the original. Always copy first for immutable code:

const sorted = [...arr].sort((a, b) => a - b);
// Or ES2023:
const sorted = arr.toSorted((a, b) => a - b);

toSorted(compareFunction) (ES2023)

Non-mutating version of sort:

const original = [3, 1, 4, 1, 5];
const sorted = original.toSorted((a, b) => a - b);
// sorted: [1, 1, 3, 4, 5]
// original unchanged: [3, 1, 4, 1, 5]

Quiz: Has Null

Use some to check if [1, null, 3] contains null.

Answer
[1, null, 3].some(v => v === null); // true

reduce(callback, initialValue) — The Swiss Army Knife

The most flexible array method. Reduces the array to a single value by accumulating.

Signature: reduce((accumulator, element, index, array) => nextAccumulator, initialValue)

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

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

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

// Max
nums.reduce((acc, n) => Math.max(acc, n), -Infinity);
// 5

Without initialValue: reduce uses the first element as the accumulator and starts from index 1.

[1, 2, 3].reduce((acc, n) => acc + n);
// 6 — same as with 0, but only works for non-empty arrays

⚠️ Always provide an initial value — it’s clearer and avoids the TypeError on empty arrays:

[].reduce((acc, n) => acc + n, 0);       // 0 — safe
[].reduce((acc, n) => acc + n);           // TypeError!

Common patterns:

// Group by
const byDept = people.reduce((groups, p) => {
  groups[p.dept] = groups[p.dept] || [];
  groups[p.dept].push(p);
  return groups;
}, {});

// Count occurrences
const counts = words.reduce((acc, w) => {
  acc[w] = (acc[w] || 0) + 1;
  return acc;
}, {});

// Tally
const colors = ['red', 'blue', 'red', 'green', 'blue', 'red'];
colors.reduce((tally, color) => {
  tally[color] = (tally[color] || 0) + 1;
  return tally;
}, {});
// { red: 3, blue: 2, green: 1 }

Object.groupBy(array, keyFn) (ES2024)

Built-in grouping — cleaner than reduce for this specific use case:

const grouped = Object.groupBy(products, p => p.category);
// {
//   fruit: [{ name: 'Apple' }, { name: 'Banana' }],
//   vegetable: [{ name: 'Carrot' }]
// }

reverse() — ⚠️ Mutates

Reverses in place:

const arr = [1, 2, 3];
arr.reverse();
// arr: [3, 2, 1]
// returns [3, 2, 1]

Non-mutating alternatives:

const reversed = [...arr].reverse();
// Or ES2023:
const reversed = arr.toReversed();

Quiz: All True

Check if [true, true, false, true] has all true values.

Answer
[true, true, false, true].every(v => v === true);
// Or simply:
[true, true, false, true].every(Boolean);

Quiz: Sort by Word Length

const words = ['apple', 'hi', 'banana', 'cat'];
// Sort by word length ascending
Answer
[...words].sort((a, b) => a.length - b.length);
// ['hi', 'cat', 'apple', 'banana']

Level 8 — Advanced Reduce, ReduceRight & Immutable Updates

Quiz: Implement filter with reduce

Solution
const filter = (arr, callback) =>
  arr.reduce((acc, el, i, a) => {
    if (callback(el, i, a)) acc.push(el);
    return acc;
  }, []);

Quiz: Sum Squares of Odds

Chain filter, map, and reduce to sum the squares of odd numbers from [1, 2, 3, 4, 5].

Solution
const sumSquaresOfOdds = arr =>
  arr
    .filter(n => n % 2 !== 0)
    .map(n => n * n)
    .reduce((a, b) => a + b, 0);

sumSquaresOfOdds([1, 2, 3, 4, 5]); // 1 + 9 + 25 = 35

Single reduce version:

arr.reduce((acc, n) => (n % 2 !== 0 ? acc + n * n : acc), 0);

Quiz: Implement join with reduce

Solution
const join = (arr, separator = ',') =>
  arr.reduce((acc, el, i) =>
    i === 0 ? String(el) : `${acc}${separator}${String(el)}`
  , '');

reduceRight(callback, initialValue)

Like reduce but iterates right-to-left:

const flattened = [[1, 2], [3, 4], [5]].reduceRight(
  (acc, arr) => acc.concat(arr), []
);
// [5, 3, 4, 1, 2]

Useful for:

  • Reversing order of operations
  • Right-associative reductions (e.g., exponentiation chains)

Quiz: Implement reverse with reduceRight

Solution
const reverse = arr =>
  arr.reduceRight((acc, el) => (acc.push(el), acc), []);

ES2023 Immutable Methods Summary

All return new arrays, none mutate:

Mutating Immutable equivalent
sort() toSorted()
reverse() toReversed()
splice() toSpliced()
arr[index] = val with(index, val)
const arr = ['a', 'b', 'c', 'd'];

arr.toReversed();           // ['d', 'c', 'b', 'a']
arr.toSorted();             // ['a', 'b', 'c', 'd']
arr.toSpliced(1, 2, 'x');   // ['a', 'x', 'd']
arr.with(1, 'z');            // ['a', 'z', 'c', 'd']
// arr unchanged

Iterators: keys(), values(), entries()

const arr = ['x', 'y', 'z'];

[...arr.keys()];     // [0, 1, 2]
[...arr.values()];   // ['x', 'y', 'z']
[...arr.entries()];  // [[0, 'x'], [1, 'y'], [2, 'z']]

Useful with for...of:

for (const [index, value] of arr.entries()) {
  console.log(index, value);
}

Appendix A: All Array Methods at a Glance

Static Methods

Method Description
Array.from(iterable, mapFn?) Creates array from iterable or array-like
Array.fromAsync(iterable, mapFn?) Async version of from
Array.isArray(value) Checks if value is an array
Array.of(...elements) Creates array from arguments

Instance Methods — Mutating

Method Description Return value
copyWithin(target, start, end?) Copies sequence within array Mutated array
fill(value, start?, end?) Fills with static value Mutated array
pop() Removes last element Removed element
push(...items) Appends elements New length
reverse() Reverses in place Mutated array
shift() Removes first element Removed element
sort(compareFn?) Sorts in place Mutated array
splice(start, deleteCount?, ...items) Add/remove at index Removed elements
unshift(...items) Prepends elements New length

Instance Methods — Non-Mutating (Accessors)

Method Description
at(index) Element at index (supports negatives)
concat(...values) Merged new array
includes(value, fromIndex?) Boolean membership test
indexOf(value, fromIndex?) First index or -1
join(separator?) String from elements
lastIndexOf(value, fromIndex?) Last index or -1
slice(start?, end?) Shallow copy segment
toString() Comma-separated string
toLocaleString() Locale-aware string

Instance Methods — Iteration (Non-Mutating)

Method Description
every(callback) All match? (short-circuits)
filter(callback) New array of matches
find(callback) First match or undefined
findIndex(callback) First match index or -1
findLast(callback) Last match or undefined
findLastIndex(callback) Last match index or -1
flat(depth?) Flattened new array
flatMap(callback) Map then flatten 1 level
forEach(callback) Side effects (returns undefined)
map(callback) Transformed new array
reduce(callback, initial?) Accumulated value (L-to-R)
reduceRight(callback, initial?) Accumulated value (R-to-L)
some(callback) Any match? (short-circuits)

Instance Methods — ES2023+ (Non-Mutating)

Method Description
toReversed() Reversed copy
toSorted(compareFn?) Sorted copy
toSpliced(start, deleteCount?, ...items) Spliced copy
with(index, value) Element-replaced copy

Instance Methods — Iterators

Method Description
entries() Iterator of [index, value] pairs
keys() Iterator of indices
values() Iterator of values
[Symbol.iterator]() Same as values()

Appendix B: Common Patterns Cheat Sheet

// Remove duplicates
const unique = [...new Set(arr)];

// Remove duplicates by property
const uniqueById = arr.filter(
  (item, i, a) => a.findIndex(t => t.id === item.id) === i
);

// Intersection
const intersection = a.filter(x => b.includes(x));

// Difference
const diff = a.filter(x => !b.includes(x));

// Chunk into groups
const chunk = (arr, n) =>
  Array.from({ length: Math.ceil(arr.length / n) }, (_, i) =>
    arr.slice(i * n, i * n + n)
  );

// Zip
const zip = (a, b) => a.map((item, i) => [item, b[i]]);

// Flatten one level
const flatten = arr => [].concat(...arr);

// Partition
const partition = (arr, callback) =>
  arr.reduce(
    ([pass, fail], el) =>
      callback(el) ? [[...pass, el], fail] : [pass, [...fail, el]],
    [[], []]
  );

// Shuffle (Fisher-Yates)
const shuffle = arr => {
  const result = [...arr];
  for (let i = result.length - 1; i > 0; i--) {
    const j = Math.floor(Math.random() * (i + 1));
    [result[i], result[j]] = [result[j], result[i]];
  }
  return result;
};

Appendix C: Performance & Gotchas

When NOT to use array methods

Situation Prefer
Need to break early for...of or some/every
Very large arrays (>100k) for loop (3-10x faster)
Sparse arrays with map Array.from or fill first
Need early return from forEach for...of or find

array.every() and empty arrays

[].every(fn) returns true (vacuous truth). [].some(fn) returns false.

NaN behavior

[NaN].includes(NaN);   // true
[NaN].indexOf(NaN);    // -1
[NaN].find(n => Number.isNaN(n)); // NaN

The thisArg parameter

All iterative methods accept a second argument for this inside the callback:

const checker = { threshold: 5 };
[1, 2, 6, 3].filter(function(el) {
  return el > this.threshold;
}, checker);
// [6]

Arrow functions ignore thisArg — they capture this lexically.

Memoized length

Iterative methods memorize length before looping. Mutations that change length during iteration produce surprising results — avoid this pattern.


Course structure inspired by Execute Program’s JavaScript Arrays curriculum. Content expanded with MDN documentation, practical patterns, and deep-dive explanations.