Home Module 12 Arrays

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

  1. Use const for arrays — the reference stays fixed; mutation is still possible and expected.
  2. Prefer for...of for simple iteration — cleaner than classic for loops; use forEach when you need the index in a callback.
  3. Always pass a comparator to sort() when sorting numbers.
  4. Use spread [...arr] to copy before mutating, when you need to keep the original.
  5. Use findIndex not indexOf when searching an array of objects.
  6. Keep arrays homogeneous — all items the same type for predictable behaviour and readable code.

8. Practice Exercise

  1. 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).
  2. Create an array of strings. Remove duplicates to produce a new array with unique values only.
  3. Given ['a', 'b', 'c', 'd', 'e'], use splice to replace 'c' with 'X' and 'Y'. Log the result.

9. Assignment

Build a "Grade Tracker" application.

  1. Store student grades in an array (numbers 0–100).
  2. Allow the user to add a grade, remove the last grade, and clear all grades using buttons.
  3. Display: all grades as a list, the count, the sum, the average (formatted to 1 decimal), the highest, and the lowest.
  4. Show a grade letter (A/B/C/D/F) next to the average based on the range.
  5. Sort grades descending when a "Sort" button is clicked.

Deliverable: One HTML file.

10. Interview Questions

  1. 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.
  2. 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.
  3. 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.
  4. 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()