Creating Arrays
JavaScript gives you two primary ways to create arrays: the array literal syntax (preferred) and the Array() constructor.
// Array literal β the preferred way
const fruits = ['apple', 'banana', 'cherry'];
const numbers = [1, 2, 3, 4, 5];
const mixed = [1, 'hello', true, null, { id: 1 }];
const empty = [];
// Array() constructor
const zeros = new Array(3); // [empty Γ 3] β sparse array
const explicit = new Array(1, 2, 3); // [1, 2, 3]
// Array.of() β always creates elements, never a sparse array
const safe = Array.of(3); // [3] (not [empty Γ 3])
// Array.from() β from iterables or array-like objects
const fromString = Array.from('hello'); // ['h','e','l','l','o']
const fromSet = Array.from(new Set([1, 2, 2, 3])); // [1, 2, 3]
const mapped = Array.from({ length: 5 }, (_, i) => i * 2);
// [0, 2, 4, 6, 8]
console.log(fruits.length); // 3
Always use [] instead of new Array(). The constructor behaves inconsistently β new Array(3) creates a sparse array with 3 empty slots, not an array containing the number 3.
Accessing Elements
Array elements are accessed by their zero-based index. ES2022 introduced Array.prototype.at() which supports negative indices.
const colors = ['red', 'green', 'blue', 'yellow'];
// Bracket notation (zero-based index)
console.log(colors[0]); // 'red'
console.log(colors[2]); // 'blue'
console.log(colors[10]); // undefined (no error)
// Negative indices with .at()
console.log(colors.at(-1)); // 'yellow' (last element)
console.log(colors.at(-2)); // 'blue'
// Array length
console.log(colors.length); // 4
// Last element (classic approach)
console.log(colors[colors.length - 1]); // 'yellow'
// Destructuring (preview)
const [first, second] = colors;
console.log(first, second); // 'red' 'green'
red blue undefined yellow blue 4 yellow red green
Adding and Removing Elements
JavaScript arrays are mutable. Several methods let you add or remove elements from either end or anywhere in the middle.
const stack = ['a', 'b', 'c'];
// End of array
stack.push('d', 'e'); // adds to end β ['a','b','c','d','e']
stack.pop(); // removes last β returns 'e' β ['a','b','c','d']
// Beginning of array
stack.unshift('z'); // adds to front β ['z','a','b','c','d']
stack.shift(); // removes first β returns 'z' β ['a','b','c','d']
// splice(startIndex, deleteCount, ...itemsToInsert)
const arr = [1, 2, 3, 4, 5];
const removed = arr.splice(1, 2); // removes 2 at index 1
console.log(removed); // [2, 3]
console.log(arr); // [1, 4, 5]
arr.splice(1, 0, 10, 20); // insert at index 1, delete 0
console.log(arr); // [1, 10, 20, 4, 5]
// slice β returns a NEW array (does not mutate)
const sliced = arr.slice(1, 3); // from index 1 up to (not including) 3
console.log(sliced); // [10, 20]
console.log(arr); // unchanged: [1, 10, 20, 4, 5]
| Method | Where | Mutates? | Returns |
|---|---|---|---|
push(...items) | End | Yes | New length |
pop() | End | Yes | Removed element |
unshift(...items) | Start | Yes | New length |
shift() | Start | Yes | Removed element |
splice(i, n, ...) | Any | Yes | Removed elements |
slice(s, e) | Any | No | New array |
Searching Arrays
JavaScript provides several methods for finding elements. Choose based on what you need: the index, the element itself, or just a boolean.
const nums = [10, 20, 30, 20, 40];
const users = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
{ id: 3, name: 'Carol' }
];
// indexOf / lastIndexOf β use strict equality (===)
console.log(nums.indexOf(20)); // 1 (first occurrence)
console.log(nums.lastIndexOf(20)); // 3 (last occurrence)
console.log(nums.indexOf(99)); // -1 (not found)
// includes β returns boolean
console.log(nums.includes(30)); // true
console.log(nums.includes(99)); // false
// find β returns the element (or undefined)
const bob = users.find(u => u.name === 'Bob');
console.log(bob); // { id: 2, name: 'Bob' }
// findIndex β returns the index (or -1)
const idx = users.findIndex(u => u.id === 3);
console.log(idx); // 2
// findLast / findLastIndex (ES2023)
const lastEven = nums.findLast(n => n % 2 === 0);
console.log(lastEven); // 40
Iterating Arrays
forEach, map, filter, and reduce are the four workhorses of array iteration. Each serves a distinct purpose.
const prices = [100, 250, 75, 320, 180];
// forEach β side effects only, returns undefined
prices.forEach((price, index) => {
console.log(`Item ${index}: $${price}`);
});
// map β transforms each element, returns new array
const discounted = prices.map(p => p * 0.9);
console.log(discounted); // [90, 225, 67.5, 288, 162]
// filter β keeps elements where callback returns true
const expensive = prices.filter(p => p > 150);
console.log(expensive); // [250, 320, 180]
// reduce β accumulates a single value
const total = prices.reduce((sum, p) => sum + p, 0);
console.log(total); // 925
// Chaining β readable pipeline
const totalDiscountExpensive = prices
.filter(p => p > 100)
.map(p => p * 0.9)
.reduce((sum, p) => sum + p, 0);
console.log(totalDiscountExpensive.toFixed(2)); // "674.10"
Item 0: $100 Item 1: $250 ... [90, 225, 67.5, 288, 162] [250, 320, 180] 925 "674.10"
Sorting and Reversing
sort() and reverse() mutate the original array. Always provide a comparator function to sort() for numeric sorting.
// Default sort β converts to string, lexicographic order
[10, 9, 2, 100].sort(); // [10, 100, 2, 9] β WRONG for numbers!
// Numeric sort with comparator
[10, 9, 2, 100].sort((a, b) => a - b); // [2, 9, 10, 100] ascending
[10, 9, 2, 100].sort((a, b) => b - a); // [100, 10, 9, 2] descending
// Alphabetical sort (strings already work without comparator)
['banana', 'apple', 'cherry'].sort(); // ['apple', 'banana', 'cherry']
// Case-insensitive sort
['Banana', 'apple', 'Cherry'].sort((a, b) =>
a.toLowerCase().localeCompare(b.toLowerCase())
);
// ['apple', 'Banana', 'Cherry']
// reverse β reverses in place
const arr = [1, 2, 3, 4, 5];
arr.reverse();
console.log(arr); // [5, 4, 3, 2, 1]
// Non-mutating sort (ES2023: toSorted, toReversed)
const original = [3, 1, 2];
const sorted = original.toSorted((a, b) => a - b);
console.log(original); // [3, 1, 2] β unchanged
console.log(sorted); // [1, 2, 3]
Without a comparator, sort() converts every element to a string first. [10, 9, 100].sort() gives [10, 100, 9] β not what you want. Always pass (a, b) => a - b for ascending numeric sort.
flat, flatMap, Array.from, Array.isArray
// flat β flattens 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)); // fully flatten any depth
// flatMap β map then flat(1) in one pass (more efficient)
const sentences = ['Hello world', 'foo bar'];
const words = sentences.flatMap(s => s.split(' '));
console.log(words); // ['Hello', 'world', 'foo', 'bar']
// Array.from β converts iterables and array-likes
const nodeList = document.querySelectorAll('p'); // NodeList (array-like)
const paragraphs = Array.from(nodeList); // real Array
// Array.from with map function
const squares = Array.from({ length: 5 }, (_, i) => (i + 1) ** 2);
console.log(squares); // [1, 4, 9, 16, 25]
// Array.isArray β reliable type check
console.log(Array.isArray([])); // true
console.log(Array.isArray(new Array())); // true
console.log(Array.isArray('hello')); // false
console.log(Array.isArray({ length: 3 })); // false
Multidimensional Arrays
JavaScript does not have true 2D arrays, but you can nest arrays inside arrays to simulate matrices and grids.
// 3x3 matrix
const matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
];
// Access with double indexing
console.log(matrix[1][2]); // 6 (row 1, col 2)
// Iterate with nested loops
for (let row = 0; row < matrix.length; row++) {
for (let col = 0; col < matrix[row].length; col++) {
process.stdout.write(matrix[row][col] + ' ');
}
console.log();
}
// Create a dynamic 2D array
function create2D(rows, cols, fill = 0) {
return Array.from({ length: rows }, () => new Array(cols).fill(fill));
}
const grid = create2D(3, 4, '.');
console.log(grid);
// [
// ['.', '.', '.', '.'],
// ['.', '.', '.', '.'],
// ['.', '.', '.', '.']
// ]
// Spread to copy a 2D array (shallow β rows are still shared)
const copy = matrix.map(row => [...row]); // deep enough for primitives
Use [...arr] or arr.slice() for a shallow copy of a 1D array. For nested arrays, use arr.map(row => [...row]) to copy each inner array too. For deeply nested structures, consider structuredClone().
Interview Questions
- What is the difference between
sliceandsplice? - Why does
[1, 10, 2].sort()return[1, 10, 2]without a comparator? - How would you remove duplicates from an array?
- What is the difference between
mapandforEach? - How do you flatten a deeply nested array in one line?
- Explain
reduceand give an example beyond summing numbers.
ποΈ Practical Exercise
Given an array of product objects [{ name, price, category }]:
- Filter products under $50.
- Map to return just the product names.
- Sort names alphabetically.
- Join them into a comma-separated string.
Do all four steps as a single chained expression.
π₯ Challenge Exercise
Write a function groupBy(arr, key) that uses reduce to group an array of objects by a given property. For example, groupBy(products, 'category') should return an object where each key is a category and its value is an array of matching products. Do not use any external library.
Frequently Asked Questions
- Is an array an object in JavaScript?
- Yes.
typeof []returns"object". Arrays are specialised objects with numeric keys and alengthproperty. UseArray.isArray()to reliably distinguish them from plain objects. - Can arrays hold mixed types?
- Yes. A single array can hold numbers, strings, booleans, objects, functions, or other arrays. However, mixing types can make code harder to reason about β consider typed arrays (
Int32Array, etc.) when working with large numeric data sets. - Does
mapmutate the original array? - No.
mapalways returns a new array. The original remains unchanged. Onlysort,reverse,push,pop,shift,unshift,splice, andfillmutate in place. - What is the difference between
findandfilter? findreturns the first matching element (orundefined).filterreturns a new array of all matching elements. Usefindwhen you expect a single result and performance matters.- How do I remove duplicates from an array?
- The easiest way is
[...new Set(arr)]. This converts the array to aSet(which only stores unique values) and spreads it back into an array.
π Summary
- Use array literals
[]to create arrays;Array.from()for iterables. - Access elements by zero-based index; use
.at(-1)for the last element. push/popwork at the end;unshift/shiftat the start;spliceanywhere.slicecreates a new array without mutating the original.- Use
indexOf/includesfor primitives;find/findIndexfor objects. - Chain
filter β map β reducefor elegant data pipelines. - Always pass a comparator to
sort()when sorting numbers. - Use
flat()andflatMap()to work with nested arrays.