JavaScript arrays are powerful data structures that allow you to store and manipulate collections of values. Arrays are dynamic, can hold mixed data types, and provide numerous methods for efficient data manipulation.
In this tutorial, we'll explore JavaScript array creation, manipulation methods, iteration techniques, and best practices for working with arrays in modern JavaScript.
Creating Arrays
Arrays can be created in multiple ways, each suited for different scenarios.
Array Literals
// Array literal syntax
const fruits = ['apple', 'banana', 'orange'];
const numbers = [1, 2, 3, 4, 5];
const mixed = ['text', 42, true, null, undefined];
// Empty array
const emptyArray = [];
// Array with expressions
const calculated = [1 + 2, 3 * 4, 5 - 1];
console.log(calculated); // [3, 12, 4]
// Nested arrays
const nested = [[1, 2], [3, 4], [5, 6]];
console.log(nested[0][1]); // 2
Array Constructor
// Array constructor
const array1 = new Array(); // Empty array
const array2 = new Array(3); // Array with 3 empty slots
const array3 = new Array('a', 'b', 'c'); // Array with elements
// Be careful with single numeric argument
const confusing = new Array(5); // Array with 5 empty slots
const notConfusing = new Array(5, 10, 15); // Array [5, 10, 15]
// Array.of() - always creates array with arguments
const better = Array.of(5); // [5]
const alsoBetter = Array.of(5, 10, 15); // [5, 10, 15]
Array.from()
// Create array from array-like objects
const str = 'hello';
const charArray = Array.from(str);
console.log(charArray); // ['h', 'e', 'l', 'l', 'o']
// Create array from NodeList
const divs = document.querySelectorAll('div');
const divArray = Array.from(divs);
// With mapping function
const numbers = [1, 2, 3, 4];
const doubled = Array.from(numbers, x => x * 2);
console.log(doubled); // [2, 4, 6, 8]
// Create array from object with length
const arrayLike = { 0: 'a', 1: 'b', 2: 'c', length: 3 };
const fromObject = Array.from(arrayLike);
console.log(fromObject); // ['a', 'b', 'c']
Basic Array Operations
Fundamental operations for accessing and modifying array elements.
Accessing Elements
// Access by index (0-based)
const fruits = ['apple', 'banana', 'orange'];
console.log(fruits[0]); // 'apple'
console.log(fruits[1]); // 'banana'
console.log(fruits[2]); // 'orange'
// Access last element
console.log(fruits[fruits.length - 1]); // 'orange'
// Accessing non-existent index
console.log(fruits[3]); // undefined
// Array length
console.log(fruits.length); // 3
// Modifying elements
fruits[1] = 'grape';
console.log(fruits); // ['apple', 'grape', 'orange']
// Adding elements at specific positions
fruits[3] = 'kiwi';
console.log(fruits); // ['apple', 'grape', 'orange', 'kiwi']
Array Properties
// Length property
const arr = [1, 2, 3, 4, 5];
console.log(arr.length); // 5
// Setting length truncates array
arr.length = 3;
console.log(arr); // [1, 2, 3]
// Increasing length adds empty slots
arr.length = 5;
console.log(arr); // [1, 2, 3, , ]
// Check if something is an array
console.log(Array.isArray(arr)); // true
console.log(Array.isArray('not an array')); // false
Adding and Removing Elements
Methods for dynamically adding and removing elements from arrays.
End Operations
// push() - add to end
const fruits = ['apple', 'banana'];
fruits.push('orange');
console.log(fruits); // ['apple', 'banana', 'orange']
// push multiple elements
fruits.push('grape', 'kiwi');
console.log(fruits); // ['apple', 'banana', 'orange', 'grape', 'kiwi']
// pop() - remove from end
const lastFruit = fruits.pop();
console.log(lastFruit); // 'kiwi'
console.log(fruits); // ['apple', 'banana', 'orange', 'grape']
Beginning Operations
// shift() - remove from beginning
const numbers = [2, 3, 4, 5];
const firstNumber = numbers.shift();
console.log(firstNumber); // 2
console.log(numbers); // [3, 4, 5]
// unshift() - add to beginning
numbers.unshift(1);
console.log(numbers); // [1, 3, 4, 5]
// unshift multiple elements
numbers.unshift(-2, -1, 0);
console.log(numbers); // [-2, -1, 0, 1, 3, 4, 5]
Splice Method
// splice() - remove/add elements anywhere
const colors = ['red', 'green', 'blue', 'yellow'];
// Remove elements
const removed = colors.splice(1, 2); // Start at index 1, remove 2 elements
console.log(removed); // ['green', 'blue']
console.log(colors); // ['red', 'yellow']
// Remove and add
const colors2 = ['red', 'green', 'blue'];
colors2.splice(1, 1, 'purple', 'orange'); // Replace green
console.log(colors2); // ['red', 'purple', 'orange', 'blue']
// Add without removing
colors2.splice(2, 0, 'pink'); // Insert at index 2
console.log(colors2); // ['red', 'purple', 'orange', 'pink', 'blue']
Searching and Filtering
Methods for finding elements and creating filtered arrays.
Finding Elements
// indexOf() - find first occurrence
const fruits = ['apple', 'banana', 'orange', 'banana'];
const bananaIndex = fruits.indexOf('banana');
console.log(bananaIndex); // 1
// indexOf with start position
const secondBanana = fruits.indexOf('banana', 2);
console.log(secondBanana); // 3
// lastIndexOf() - find last occurrence
const lastBanana = fruits.lastIndexOf('banana');
console.log(lastBanana); // 3
// includes() - check if element exists
const hasApple = fruits.includes('apple');
console.log(hasApple); // true
const hasGrape = fruits.includes('grape');
console.log(hasGrape); // false
Advanced Searching
// find() - find first element matching condition
const numbers = [10, 20, 30, 40, 50];
const found = numbers.find(num => num > 25);
console.log(found); // 30
// findIndex() - find index of first matching element
const foundIndex = numbers.findIndex(num => num > 25);
console.log(foundIndex); // 2
// findLast() - find last matching element (ES2023)
const lastFound = numbers.findLast(num => num > 25);
console.log(lastFound); // 50
// findLastIndex() - find index of last matching element (ES2023)
const lastFoundIndex = numbers.findLastIndex(num => num > 25);
console.log(lastFoundIndex); // 4
Filtering
// filter() - create new array with elements passing test
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const evenNumbers = numbers.filter(num => num % 2 === 0);
console.log(evenNumbers); // [2, 4, 6, 8, 10]
const greaterThanFive = numbers.filter(num => num > 5);
console.log(greaterThanFive); // [6, 7, 8, 9, 10]
// Filter with complex conditions
const products = [
{ name: 'Laptop', price: 1000, inStock: true },
{ name: 'Phone', price: 500, inStock: false },
{ name: 'Tablet', price: 300, inStock: true }
];
const affordableProducts = products.filter(p => p.price < 600 && p.inStock);
console.log(affordableProducts); // [{ name: 'Laptop', price: 1000, inStock: true }]
Transforming Arrays
Methods that create new arrays by transforming existing ones.
Map Method
// map() - transform each element
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(num => num * 2);
console.log(doubled); // [2, 4, 6, 8, 10]
// Map to different types
const strings = numbers.map(num => num.toString());
console.log(strings); // ['1', '2', '3', '4', '5']
// Map objects
const users = [
{ firstName: 'John', lastName: 'Doe' },
{ firstName: 'Jane', lastName: 'Smith' }
];
const fullNames = users.map(user => `${user.firstName} ${user.lastName}`);
console.log(fullNames); // ['John Doe', 'Jane Smith']
// Map with index
const withIndex = numbers.map((num, index) => `${index}: ${num}`);
console.log(withIndex); // ['0: 1', '1: 2', '2: 3', '3: 4', '4: 5']
Flat and FlatMap
// flat() - flatten nested arrays
const nested = [[1, 2], [3, 4], [5, 6]];
const flattened = nested.flat();
console.log(flattened); // [1, 2, 3, 4, 5, 6]
// flat with depth
const deeplyNested = [[1, [2, 3]], [4, [5, [6]]]];
const flattenedDeep = deeplyNested.flat(2);
console.log(flattenedDeep); // [1, 2, 3, 4, 5, [6]]
// flatMap() - map then flatten
const sentences = ['Hello world', 'How are you', 'Goodbye'];
const words = sentences.flatMap(sentence => sentence.split(' '));
console.log(words); // ['Hello', 'world', 'How', 'are', 'you', 'Goodbye']
// flatMap vs map + flat
const numbers2 = [1, 2, 3];
const mappedAndFlat = numbers2.map(n => [n, n * 2]).flat();
console.log(mappedAndFlat); // [1, 2, 2, 4, 3, 6]
const flatMapped = numbers2.flatMap(n => [n, n * 2]);
console.log(flatMapped); // [1, 2, 2, 4, 3, 6]
Reducing Arrays
Methods that reduce arrays to single values.
Reduce Method
// reduce() - reduce array to single value
const numbers = [1, 2, 3, 4, 5];
const sum = numbers.reduce((accumulator, current) => accumulator + current, 0);
console.log(sum); // 15
// Reduce without initial value
const sum2 = numbers.reduce((acc, curr) => acc + curr);
console.log(sum2); // 15 (uses first element as initial value)
// Find maximum
const max = numbers.reduce((max, current) => Math.max(max, current));
console.log(max); // 5
// Group by property
const fruits = [
{ name: 'apple', type: 'fruit' },
{ name: 'carrot', type: 'vegetable' },
{ name: 'banana', type: 'fruit' }
];
const grouped = fruits.reduce((groups, fruit) => {
const type = fruit.type;
if (!groups[type]) {
groups[type] = [];
}
groups[type].push(fruit.name);
return groups;
}, {});
console.log(grouped);
// { fruit: ['apple', 'banana'], vegetable: ['carrot'] }
Reduce Right
// reduceRight() - reduce from right to left
const numbers = [1, 2, 3, 4];
const sumRight = numbers.reduceRight((acc, curr) => acc + curr);
console.log(sumRight); // 10
// Useful for function composition
const add = a => b => a + b;
const multiply = a => b => a * b;
const divide = a => b => a / b;
const operations = [add(2), multiply(3), divide(6)];
const result = operations.reduceRight((acc, fn) => fn(acc));
console.log(result); // ((2 + 3) * 6) / 6 = 3
// Building strings from right
const words = ['world', 'beautiful', 'a', 'is', 'world'];
const sentence = words.reduceRight((acc, word) => `${word} ${acc}`);
console.log(sentence); // 'world is a beautiful world'
Testing Arrays
Methods that test conditions on array elements.
Some and Every
// some() - test if at least one element passes test
const numbers = [1, 2, 3, 4, 5];
const hasEven = numbers.some(num => num % 2 === 0);
console.log(hasEven); // true
const hasNegative = numbers.some(num => num < 0);
console.log(hasNegative); // false
// every() - test if all elements pass test
const allPositive = numbers.every(num => num > 0);
console.log(allPositive); // true
const allEven = numbers.every(num => num % 2 === 0);
console.log(allEven); // false
// Practical examples
const users = [
{ name: 'John', age: 25, active: true },
{ name: 'Jane', age: 17, active: false },
{ name: 'Bob', age: 30, active: true }
];
const hasActiveUsers = users.some(user => user.active);
console.log(hasActiveUsers); // true
const allAdults = users.every(user => user.age >= 18);
console.log(allAdults); // false
Sorting Arrays
Methods for ordering array elements.
Sort Method
// sort() - sort array in place
const fruits = ['banana', 'apple', 'orange', 'grape'];
fruits.sort();
console.log(fruits); // ['apple', 'banana', 'grape', 'orange']
// Numbers need custom compare function
const numbers = [10, 5, 100, 50, 1];
numbers.sort(); // Sorts as strings: [1, 10, 100, 5, 50]
console.log(numbers);
// Correct numeric sort
numbers.sort((a, b) => a - b);
console.log(numbers); // [1, 5, 10, 50, 100]
// Descending order
numbers.sort((a, b) => b - a);
console.log(numbers); // [100, 50, 10, 5, 1]
Advanced Sorting
// Sort objects
const people = [
{ name: 'John', age: 25 },
{ name: 'Jane', age: 20 },
{ name: 'Bob', age: 30 }
];
// Sort by age
people.sort((a, b) => a.age - b.age);
console.log(people); // Jane, John, Bob
// Sort by name
people.sort((a, b) => a.name.localeCompare(b.name));
console.log(people); // Bob, Jane, John
// Sort with multiple criteria
const products = [
{ name: 'A', price: 100, category: 'electronics' },
{ name: 'B', price: 50, category: 'books' },
{ name: 'C', price: 75, category: 'electronics' }
];
products.sort((a, b) => {
if (a.category !== b.category) {
return a.category.localeCompare(b.category);
}
return a.price - b.price;
});
Reverse
// reverse() - reverse array in place
const numbers = [1, 2, 3, 4, 5];
numbers.reverse();
console.log(numbers); // [5, 4, 3, 2, 1]
// Combined with sort for descending
const fruits = ['apple', 'banana', 'orange'];
fruits.sort().reverse();
console.log(fruits); // ['orange', 'banana', 'apple']
// toReversed() - non-mutating alternative (ES2023)
const original = [1, 2, 3, 4, 5];
const reversed = original.toReversed();
console.log(original); // [1, 2, 3, 4, 5] (unchanged)
console.log(reversed); // [5, 4, 3, 2, 1]
Array Iteration
Different ways to iterate over array elements.
ForEach Method
// forEach() - execute function for each element
const fruits = ['apple', 'banana', 'orange'];
fruits.forEach((fruit, index) => {
console.log(`${index + 1}: ${fruit}`);
});
// With side effects
const numbers = [1, 2, 3, 4, 5];
let sum = 0;
numbers.forEach(num => {
sum += num;
});
console.log(sum); // 15
// forEach vs map
const doubled = [];
numbers.forEach(num => {
doubled.push(num * 2);
});
console.log(doubled); // [2, 4, 6, 8, 10]
// Better use map for transformation
const doubledMap = numbers.map(num => num * 2);
console.log(doubledMap); // [2, 4, 6, 8, 10]
Traditional Loops
// for loop
const fruits = ['apple', 'banana', 'orange'];
for (let i = 0; i < fruits.length; i++) {
console.log(fruits[i]);
}
// for...of loop (ES6)
for (const fruit of fruits) {
console.log(fruit);
}
// for...of with index
for (const [index, fruit] of fruits.entries()) {
console.log(`${index}: ${fruit}`);
}
// for...in loop (not recommended for arrays)
for (const index in fruits) {
console.log(index, fruits[index]);
}
Iteration Methods
// keys() - get iterator for keys
const fruits = ['apple', 'banana', 'orange'];
for (const key of fruits.keys()) {
console.log(key); // 0, 1, 2
}
// values() - get iterator for values
for (const value of fruits.values()) {
console.log(value); // 'apple', 'banana', 'orange'
}
// entries() - get iterator for key-value pairs
for (const [key, value] of fruits.entries()) {
console.log(`${key}: ${value}`);
}
Array Destructuring
Modern JavaScript syntax for extracting values from arrays.
Basic Destructuring
// Array destructuring
const fruits = ['apple', 'banana', 'orange'];
const [first, second, third] = fruits;
console.log(first, second, third); // 'apple', 'banana', 'orange'
// Skip elements
const [, , thirdOnly] = fruits;
console.log(thirdOnly); // 'orange'
// Rest operator
const [firstFruit, ...restFruits] = fruits;
console.log(firstFruit); // 'apple'
console.log(restFruits); // ['banana', 'orange']
// Default values
const [a, b, c = 'default'] = ['x', 'y'];
console.log(a, b, c); // 'x', 'y', 'default'
Swapping Variables
// Swap variables without temp variable
let a = 1;
let b = 2;
[a, b] = [b, a];
console.log(a, b); // 2, 1
// Function return destructuring
function getCoordinates() {
return [10, 20];
}
const [x, y] = getCoordinates();
console.log(x, y); // 10, 20
Array Performance
Understanding performance characteristics of array operations.
Time Complexity
// Fast operations (O(1))
const arr = [1, 2, 3, 4, 5];
arr.push(6); // O(1) - add to end
arr.pop(); // O(1) - remove from end
arr[0]; // O(1) - access by index
arr.length; // O(1) - get length
// Slow operations (O(n))
arr.shift(); // O(n) - remove from beginning
arr.unshift(0); // O(n) - add to beginning
arr.indexOf(3); // O(n) - search
arr.splice(2, 1); // O(n) - remove from middle
// Use appropriate methods for performance
const bigArray = new Array(10000).fill(0);
// Good: push/pop
bigArray.push(1); // Fast
bigArray.pop(); // Fast
// Avoid: shift/unshift on large arrays
// bigArray.shift(); // Slow on large arrays
// bigArray.unshift(0); // Slow on large arrays
Memory Considerations
// Methods that create new arrays
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(n => n * 2); // New array
const filtered = numbers.filter(n => n > 2); // New array
// Methods that modify in place
numbers.push(6); // Modifies original
numbers.pop(); // Modifies original
// For large arrays, consider memory usage
const largeArray = new Array(1000000).fill(0);
// This creates a new array - memory intensive
const processed = largeArray.map(x => x + 1);
// Consider processing in chunks for very large arrays
function processInChunks(array, chunkSize, processor) {
const results = [];
for (let i = 0; i < array.length; i += chunkSize) {
const chunk = array.slice(i, i + chunkSize);
const processedChunk = chunk.map(processor);
results.push(...processedChunk);
}
return results;
}
Modern Array Methods
Recent additions to JavaScript array methods.
ES2023+ Methods
// toSorted() - non-mutating sort
const numbers = [3, 1, 4, 1, 5];
const sorted = numbers.toSorted();
console.log(numbers); // [3, 1, 4, 1, 5] (unchanged)
console.log(sorted); // [1, 1, 3, 4, 5]
// toSpliced() - non-mutating splice
const arr = [1, 2, 3, 4, 5];
const spliced = arr.toSpliced(1, 2, 99);
console.log(arr); // [1, 2, 3, 4, 5] (unchanged)
console.log(spliced); // [1, 99, 4, 5]
// with() - non-mutating element replacement
const original = [1, 2, 3, 4, 5];
const withReplaced = original.with(2, 99);
console.log(original); // [1, 2, 3, 4, 5] (unchanged)
console.log(withReplaced); // [1, 2, 99, 4, 5]
// findLast() and findLastIndex()
const numbers2 = [1, 2, 3, 4, 5];
const lastEven = numbers2.findLast(n => n % 2 === 0);
console.log(lastEven); // 4
const lastEvenIndex = numbers2.findLastIndex(n => n % 2 === 0);
console.log(lastEvenIndex); // 3
Best Practices
Guidelines for writing clean and efficient array code.
Choose the Right Method
// Use map for transformation
const numbers = [1, 2, 3];
const doubled = numbers.map(n => n * 2); // Good
// Use filter for selection
const evens = numbers.filter(n => n % 2 === 0); // Good
// Use reduce for aggregation
const sum = numbers.reduce((acc, n) => acc + n, 0); // Good
// Avoid mutating when possible
const original = [1, 2, 3];
const processed = original.map(n => n * 2); // Better than modifying original
// Use appropriate iteration method
// forEach for side effects
numbers.forEach(n => console.log(n));
// map for transformation
const transformed = numbers.map(n => n * 2);
// filter for selection
const filtered = numbers.filter(n => n > 2);
Immutable Patterns
// Immutable array updates
const state = [1, 2, 3];
// Add item (immutable)
const newState = [...state, 4]; // [1, 2, 3, 4]
// Remove item (immutable)
const withoutLast = state.slice(0, -1); // [1, 2]
// Update item (immutable)
const updated = state.map((item, index) =>
index === 1 ? 99 : item
); // [1, 99, 3]
// Filter item (immutable)
const filtered = state.filter(item => item !== 2); // [1, 3]
Error Handling
// Handle potential undefined values
const arr = [1, 2, undefined, 4];
const doubled = arr.map(x => x ? x * 2 : 0); // Handle undefined
// Use filter to remove unwanted values
const clean = arr.filter(x => x !== undefined);
// Validate array before operations
function processArray(arr) {
if (!Array.isArray(arr)) {
throw new Error('Expected an array');
}
return arr.map(x => x * 2);
}
// Use optional chaining with array methods
const data = { items: [1, 2, 3] };
const doubled = data.items?.map(x => x * 2) || [];
Summary
Key Takeaways
- Arrays are dynamic, can hold mixed types, and provide numerous methods
- Choose appropriate methods: map for transformation, filter for selection
- Modern methods like toSorted(), toSpliced() provide immutable alternatives
- Consider performance: push/pop are fast, shift/unshift are slow
- Use destructuring and spread operators for cleaner code
Best Practices
- Prefer immutable patterns when possible
- Use appropriate iteration methods for different use cases
- Handle edge cases like undefined values
- Consider performance for large arrays
- Use modern ES6+ features for cleaner code
Common Pitfalls
- Using sort() without compare function for numbers
- Modifying arrays during iteration
- Using for...in loop for arrays
- Not handling undefined values in map/filter
- Creating unnecessary intermediate arrays