Arrays
Store and manage ordered collections of values — the most-used data structure in JavaScript.
1. Introduction
An array is an ordered list of values. You use arrays whenever you have multiple related items: a list of products, a queue of messages, a set of scores. Arrays are zero-indexed, dynamic (they grow and shrink), and can hold any type of value — including other arrays and objects.
2. Theory
2.1 Creating arrays
// Array literal — the preferred way
const fruits = ['apple', 'banana', 'cherry'];
// Empty array then push items
const scores = [];
scores.push(95, 87, 72);
// Mixed types (possible but avoid in practice)
const mixed = [1, 'hello', true, null];
// Array constructor (rarely needed)
const fiveSlots = new Array(5); // [empty x5]
2.2 Accessing elements
const colours = ['red', 'green', 'blue'];
console.log(colours[0]); // 'red' — first element
console.log(colours[2]); // 'blue' — last element
console.log(colours[-1]); // undefined — negative index doesn't work in JS
console.log(colours[99]); // undefined — out of bounds
// Length property
console.log(colours.length); // 3
// Last element (reliable)
console.log(colours[colours.length - 1]); // 'blue'
console.log(colours.at(-1)); // 'blue' — newer ES2022 syntax
2.3 Modifying elements
const nums = [10, 20, 30];
nums[1] = 99; // replace index 1 → [10, 99, 30]
nums[3] = 40; // add at index 3 → [10, 99, 30, 40]
console.log(nums.length); // 4
// const arrays can still be mutated!
const list = ['a', 'b'];
list[0] = 'z'; // works — the array reference stays the same
// list = []; // TypeError — cannot reassign the binding
2.4 Adding and removing — push/pop/shift/unshift
const stack = ['a', 'b', 'c'];
// End of array
stack.push('d'); // adds to end → ['a','b','c','d']
const last = stack.pop(); // removes end → ['a','b','c'], last = 'd'
// Start of array (slower — shifts all indices)
stack.unshift('z'); // adds to start → ['z','a','b','c']
const first = stack.shift(); // removes start → ['a','b','c'], first = 'z'
2.5 splice — insert/remove at any position
const letters = ['a', 'b', 'c', 'd', 'e'];
// splice(startIndex, deleteCount, ...itemsToInsert)
letters.splice(2, 1); // remove 1 item at index 2 → ['a','b','d','e']
letters.splice(1, 0, 'X', 'Y'); // insert at index 1 → ['a','X','Y','b','d','e']
letters.splice(2, 2, 'Z'); // replace 2 items with 1 → ['a','X','Z','d','e']
// splice returns the removed items
const removed = letters.splice(0, 2);
console.log(removed); // ['a', 'X']
2.6 slice — copy a portion (non-destructive)
const arr = ['a', 'b', 'c', 'd', 'e'];
// slice(start, end) — end is exclusive, returns new array
console.log(arr.slice(1, 3)); // ['b', 'c']
console.log(arr.slice(2)); // ['c', 'd', 'e'] — to end
console.log(arr.slice(-2)); // ['d', 'e'] — last 2
console.log(arr.slice()); // ['a','b','c','d','e'] — shallow copy
console.log(arr); // original unchanged — ['a','b','c','d','e']
2.7 indexOf / includes / find
const fruits = ['apple', 'banana', 'cherry', 'banana'];
// indexOf — returns first index, -1 if not found
console.log(fruits.indexOf('banana')); // 1
console.log(fruits.indexOf('mango')); // -1
// lastIndexOf — searches from end
console.log(fruits.lastIndexOf('banana')); // 3
// includes — boolean check (uses strict equality)
console.log(fruits.includes('cherry')); // true
console.log(fruits.includes('grape')); // false
// findIndex — find by condition
const nums = [5, 12, 8, 130, 44];
const idx = nums.findIndex(n => n > 10);
console.log(idx); // 1 (index of 12)
2.8 Iterating arrays
const items = ['pen', 'book', 'ruler'];
// for...of — clean, preferred for simple iteration
for (const item of items) {
console.log(item);
}
// forEach — calls a function for each element
items.forEach((item, index) => {
console.log(`${index}: ${item}`);
});
// Classic for loop — use when you need index control
for (let i = 0; i < items.length; i++) {
console.log(items[i]);
}
2.9 Spreading and combining arrays
const a = [1, 2, 3];
const b = [4, 5, 6];
// Spread operator — merge arrays
const combined = [...a, ...b]; // [1,2,3,4,5,6]
const withExtra = [...a, 99, ...b]; // [1,2,3,99,4,5,6]
// concat — same result, older syntax
const also = a.concat(b); // [1,2,3,4,5,6]
// Copy an array (shallow)
const copy = [...a]; // or a.slice()
copy.push(99);
console.log(a); // [1,2,3] — original unaffected
console.log(copy); // [1,2,3,99]
2.10 join and split
const words = ['Hello', 'world', 'JavaScript'];
// join — array to string
console.log(words.join(' ')); // 'Hello world JavaScript'
console.log(words.join('-')); // 'Hello-world-JavaScript'
console.log(words.join('')); // 'HelloworldJavaScript'
// split — string to array (String method)
const csv = 'Alice,Bob,Charlie';
const names = csv.split(',');
console.log(names); // ['Alice', 'Bob', 'Charlie']
2.11 sort
// Default sort — converts to strings! Dangerous for numbers
const nums = [10, 1, 21, 2];
nums.sort();
console.log(nums); // [1, 10, 2, 21] — WRONG for numbers
// Correct numeric sort — provide a compare function
nums.sort((a, b) => a - b); // ascending → [1, 2, 10, 21]
nums.sort((a, b) => b - a); // descending → [21, 10, 2, 1]
// String sort (alphabetical)
const names = ['Charlie', 'Alice', 'Bob'];
names.sort(); // ['Alice', 'Bob', 'Charlie'] — correct for strings
3. Real World Example
// Shopping cart — array of product objects
const cart = [];
function addToCart(product) {
const existing = cart.findIndex(item => item.id === product.id);
if (existing >= 0) {
cart[existing].qty += 1; // update quantity
} else {
cart.push({ ...product, qty: 1 }); // add new
}
}
function removeFromCart(productId) {
const idx = cart.findIndex(item => item.id === productId);
if (idx >= 0) cart.splice(idx, 1);
}
function getTotal() {
let total = 0;
for (const item of cart) {
total += item.price * item.qty;
}
return total;
}
addToCart({ id: 1, name: 'T-Shirt', price: 19.99 });
addToCart({ id: 2, name: 'Jeans', price: 49.99 });
addToCart({ id: 1, name: 'T-Shirt', price: 19.99 }); // qty becomes 2
console.log(cart);
// [{id:1, name:'T-Shirt', price:19.99, qty:2}, {id:2, name:'Jeans', price:49.99, qty:1}]
console.log(getTotal()); // 89.97
4. Code Example
<input id="task-input" placeholder="Add task">
<button id="add-btn">Add</button>
<ul id="task-list"></ul>
<p id="summary"></p>
<script>
const tasks = [];
const input = document.querySelector('#task-input');
const addBtn = document.querySelector('#add-btn');
const list = document.querySelector('#task-list');
const summary = document.querySelector('#summary');
function render() {
list.innerHTML = '';
tasks.forEach((task, index) => {
const li = document.createElement('li');
const del = document.createElement('button');
del.textContent = 'Remove';
del.dataset.index = index;
li.textContent = task + ' ';
li.appendChild(del);
list.appendChild(li);
});
summary.textContent = `Total tasks: ${tasks.length}`;
}
addBtn.addEventListener('click', () => {
const text = input.value.trim();
if (!text) return;
tasks.push(text);
input.value = '';
render();
});
list.addEventListener('click', e => {
const btn = e.target.closest('button[data-index]');
if (!btn) return;
tasks.splice(Number(btn.dataset.index), 1);
render();
});
</script>
5. Code Breakdown
tasks array as the source of truth
The array holds all data. The DOM is just a visual representation. Every change goes to the array first, then render() rebuilds the list — this is the data-first approach used in all modern frameworks.
forEach with index
forEach receives both the item and its index. Storing the index in data-index lets the delegation handler know which task to splice out when the remove button is clicked.
splice for removal
splice(index, 1) removes exactly one element at the given position. After removal, render() rebuilds the list with updated indices — avoiding stale references.
6. Common Mistakes
Mistake 1 — Sorting numbers without a comparator
[10, 9, 2, 100].sort(); // [10, 100, 2, 9] — string sort
[10, 9, 2, 100].sort((a,b) => a - b); // [2, 9, 10, 100] — correct
Mistake 2 — Mutating the original when you need a copy
const a = [1, 2, 3];
const b = a; // b is NOT a copy — same reference!
b.push(4);
console.log(a); // [1, 2, 3, 4] — original mutated!
// Fix: use spread or slice
const c = [...a]; // or a.slice()
c.push(4);
console.log(a); // [1, 2, 3] — safe
Mistake 3 — Using indexOf for objects
const users = [{ id: 1 }, { id: 2 }];
console.log(users.indexOf({ id: 1 })); // -1 — different object reference
// Fix: use findIndex with a condition
const idx = users.findIndex(u => u.id === 1); // 0 — works
Mistake 4 — Off-by-one in loops
const arr = [1, 2, 3];
for (let i = 1; i <= arr.length; i++) { // Bug: starts at 1, goes to 3 inclusive
console.log(arr[i]); // 2, 3, undefined
}
// Fix:
for (let i = 0; i < arr.length; i++) {
console.log(arr[i]); // 1, 2, 3
}
7. Best Practices
- Use const for arrays — the reference stays fixed; mutation is still possible and expected.
- Prefer for...of for simple iteration — cleaner than classic for loops; use forEach when you need the index in a callback.
- Always pass a comparator to sort() when sorting numbers.
- Use spread [...arr] to copy before mutating, when you need to keep the original.
- Use findIndex not indexOf when searching an array of objects.
- Keep arrays homogeneous — all items the same type for predictable behaviour and readable code.
8. Practice Exercise
- Create an array of five numbers. Write code to find the largest value, the smallest value, and the sum — using only a loop (no built-in Math methods).
- Create an array of strings. Remove duplicates to produce a new array with unique values only.
- Given
['a', 'b', 'c', 'd', 'e'], usespliceto replace'c'with'X'and'Y'. Log the result.
9. Assignment
Build a "Grade Tracker" application.
- Store student grades in an array (numbers 0–100).
- Allow the user to add a grade, remove the last grade, and clear all grades using buttons.
- Display: all grades as a list, the count, the sum, the average (formatted to 1 decimal), the highest, and the lowest.
- Show a grade letter (A/B/C/D/F) next to the average based on the range.
- Sort grades descending when a "Sort" button is clicked.
Deliverable: One HTML file.
10. Interview Questions
- What is the difference between push/pop and shift/unshift?
push/pop add/remove from the END of an array. unshift/shift add/remove from the START. Start operations are slower because all indices must be renumbered. - What is the difference between splice and slice?
splice modifies the original array — it can insert, remove, or replace elements at any position. slice does NOT modify the original — it returns a new array containing a portion of the original. - Why should you pass a comparator to sort() when sorting numbers?
Without a comparator, sort converts values to strings and sorts lexicographically — so 10 comes before 2 because "1" < "2". A comparator like (a, b) => a - b returns negative/zero/positive to tell the engine the correct order. - How do you copy an array in JavaScript?
Use the spread operator [...arr] or arr.slice(). Assignment (b = a) does NOT copy — both variables point to the same array in memory.
11. Additional Resources
- MDN — Array — complete method reference
- javascript.info — Arrays
- MDN — Array.prototype.splice()
- MDN — Array.prototype.sort()