🟨 JavaScript Array Basics

Index: Basics Index | Parent: JS Array Index


1. What is a JavaScript Array?

A JavaScript array is a dynamic, ordered list of values. Unlike low-level languages, JS arrays:

Memory Model (conceptual):
Index:  [0]    [1]    [2]    [3]    [4]
Value:  [10]   [20]   [30]   [40]   [50]
        ↑ first                  ↑ last (arr.length - 1)

Key Properties:


2. Creating Arrays

// 1. Array literal (most common)
const arr = [10, 20, 30, 40, 50];

// 2. Array constructor
const arr2 = new Array(5);        // [empty Γ— 5]
const arr3 = new Array(1, 2, 3);  // [1, 2, 3]

// 3. Array.of() β€” avoids Array(n) single-arg ambiguity
const arr4 = Array.of(5);         // [5]  (not [empty Γ— 5])
const arr5 = Array.of(1, 2, 3);   // [1, 2, 3]

// 4. Array.from() β€” from iterable or array-like
const arr6 = Array.from('hello');           // ['h','e','l','l','o']
const arr7 = Array.from({length: 5}, (_, i) => i); // [0,1,2,3,4]
const arr8 = Array.from(new Set([1,2,2,3]));        // [1,2,3]

// 5. Spread from another iterable
const set = new Set([1, 2, 3]);
const arr9 = [...set];            // [1, 2, 3]

// Pre-fill array
const zeros   = new Array(5).fill(0);       // [0,0,0,0,0]
const matrix  = Array.from({length:3}, () => new Array(3).fill(0));
// [[0,0,0],[0,0,0],[0,0,0]]

3. Accessing Elements

const fruits = ['apple', 'banana', 'cherry', 'date'];

// By index
console.log(fruits[0]);      // 'apple'
console.log(fruits[3]);      // 'date'
console.log(fruits[-1]);     // undefined (JS doesn't support negative index directly)

// at() β€” supports negative indexing (ES2022)
console.log(fruits.at(-1));  // 'date'  ← last element
console.log(fruits.at(-2));  // 'cherry'

// Array destructuring
const [first, second, ...rest] = fruits;
console.log(first);  // 'apple'
console.log(second); // 'banana'
console.log(rest);   // ['cherry', 'date']

// Skip elements
const [,, third] = fruits;
console.log(third);  // 'cherry'

// Swap two variables (no temp variable needed)
let a = 1, b = 2;
[a, b] = [b, a];
console.log(a, b); // 2 1

// Default values in destructuring
const [x = 0, y = 0, z = 0] = [10, 20];
console.log(x, y, z); // 10 20 0

4. Mutating Arrays

const arr = [1, 2, 3];

// ── End operations O(1) ──────────────────────────
arr.push(4);          // [1,2,3,4] β€” add to end
arr.push(5, 6);       // [1,2,3,4,5,6] β€” multiple
const last = arr.pop(); // removes & returns last: 6

// ── Start operations O(n) ────────────────────────
arr.unshift(0);       // [0,1,2,3,4,5] β€” add to start
const first = arr.shift(); // removes & returns first: 0

// ── Middle operations: splice ────────────────────
// splice(startIndex, deleteCount, ...itemsToInsert)
arr.splice(2, 0, 99); // insert 99 at index 2, delete 0
// arr = [1,2,99,3,4,5]

arr.splice(2, 1);     // remove 1 element at index 2
// arr = [1,2,3,4,5]

arr.splice(1, 2, 10, 20); // replace 2 elements starting at 1
// arr = [1,10,20,4,5]

// ── Reverse & sort ───────────────────────────────
arr.reverse();        // mutates in place!
arr.sort();           // mutates in place β€” CAUTION: sorts as strings!

5. Spread & Rest Operator

// ── SPREAD ───────────────────────────────────────

// Clone an array (shallow)
const original = [1, 2, 3];
const clone = [...original];  // new array, not a reference
clone.push(4);
console.log(original); // [1,2,3] β€” unchanged

// Merge arrays
const a = [1, 2];
const b = [3, 4];
const merged = [...a, ...b];   // [1,2,3,4]

// Pass array as function arguments
function sum(x, y, z) { return x + y + z; }
const nums = [1, 2, 3];
console.log(sum(...nums)); // 6

// Add element without mutation
const arr = [1, 2, 3];
const newArr = [...arr, 4];       // [1,2,3,4]
const newFront = [0, ...arr];     // [0,1,2,3]

// ── REST (collect remaining) ──────────────────────
function first3(...args) {
  const [a, b, c, ...others] = args;
  return { first: [a,b,c], rest: others };
}
console.log(first3(1,2,3,4,5));
// { first: [1,2,3], rest: [4,5] }

6. Multidimensional Arrays

// 2D array (matrix)
const matrix = [
  [1, 2, 3],
  [4, 5, 6],
  [7, 8, 9]
];

// Access: matrix[row][col]
console.log(matrix[1][2]); // 6

// Iterate over 2D array
for (let r = 0; r < matrix.length; r++) {
  for (let c = 0; c < matrix[r].length; c++) {
    process.stdout.write(matrix[r][c] + ' ');
  }
  console.log();
}

// Using forEach
matrix.forEach(row => console.log(row.join(' ')));

// Create NxM matrix filled with zeros
const rows = 3, cols = 4;
const grid = Array.from({length: rows}, () => new Array(cols).fill(0));

// Common mistake: DON'T do this
const wrong = new Array(3).fill([]);  // all rows share SAME array reference!
wrong[0].push(1);
console.log(wrong); // [[1],[1],[1]] β€” BUG!

// Correct way
const correct = Array.from({length: 3}, () => []);

7. Common Mistakes

// ── 1. Sorting numbers without comparator ─────────
[10, 9, 2, 100, 21].sort();
// [10, 100, 2, 21, 9] ← WRONG! sorts as strings

[10, 9, 2, 100, 21].sort((a, b) => a - b);
// [2, 9, 10, 21, 100] ← correct

// ── 2. Shallow copy vs deep copy ─────────────────
const obj = [{a: 1}, {b: 2}];
const shallow = [...obj];
shallow[0].a = 99;
console.log(obj[0].a); // 99 ← original mutated!

// Deep clone (simple):
const deep = JSON.parse(JSON.stringify(obj));

// ── 3. Reference equality ─────────────────────────
console.log([] === []);  // false β€” different references
console.log([1,2] == [1,2]); // false

// ── 4. Holes in arrays ────────────────────────────
const arr = [1,,3];  // arr[1] is undefined (hole)
arr.length;          // 3

// ── 5. forEach has no return / break ──────────────
const result = [1,2,3].forEach(x => x * 2); // result is undefined!
// Use .map() if you need return values
const mapped = [1,2,3].map(x => x * 2); // [2,4,6]

// ── 6. Off-by-one ─────────────────────────────────
const arr2 = [10, 20, 30];
// last index is arr2.length - 1 = 2
// arr2[arr2.length] = undefined  ← common bug

8. Interview Questions


Q1. Reverse an Array [E][FB]


🧠 How to Approach This

Step 1 β€” Recognize the pattern:

Reverse = swap elements from both ends working inward β†’ two pointers (opposite direction).

Step 2 β€” Brute force:

Create new array and copy in reverse β†’ O(n) space. Interviewer often wants O(1) space.

Step 3 β€” Optimal insight:

Two pointers L=0, R=n-1. Swap, then L++, R--. Stop when L >= R.

Step 4 β€” JS shortcut:

[...arr].reverse() for non-mutating reverse (spread to avoid mutating original).

Step 5 β€” Edge cases:

- Empty array: L >= R immediately, nothing to do

- Odd length: middle stays in place

Way of Thinking: Use two pointers β€” one at start, one at end. Swap and move toward center. O(n) time, O(1) space.

function reverseArray(arr) {
  let left = 0, right = arr.length - 1;
  while (left < right) {
    [arr[left], arr[right]] = [arr[right], arr[left]]; // destructuring swap
    left++;
    right--;
  }
  return arr;
}

// One-liner (non-mutating)
const reversed = [...arr].reverse();

console.log(reverseArray([1,2,3,4,5])); // [5,4,3,2,1]

Q2. Find Max and Min [E][FB]


🧠 How to Approach This

Step 1 β€” Recognize the pattern:

Single-pass scan tracking two running values.

Step 2 β€” Key insight:

Initialize max=-Infinity, min=Infinity. Each element either updates max, updates min, or does nothing. One O(n) pass handles both.

Step 3 β€” JS shortcuts:

Math.max(...arr) works for small arrays. For large arrays (>100K elements), spread can cause stack overflow β€” use reduce instead.

Step 4 β€” Edge cases:

- Single element: max = min = that element

- All negatives: max will be a negative number

Way of Thinking: Single pass, track max/min variables. OR use Math.max(...arr) for small arrays.

function findMaxMin(arr) {
  let max = -Infinity, min = Infinity;
  for (const num of arr) {
    if (num > max) max = num;
    if (num < min) min = num;
  }
  return { max, min };
}

// One-liners
const max = Math.max(...arr);    // spread into Math.max
const min = Math.min(...arr);
// ⚠ spread can cause stack overflow for very large arrays
// Safe alternative:
const safeMax = arr.reduce((a, b) => a > b ? a : b);

console.log(findMaxMin([3, 1, 4, 1, 5, 9, 2])); // { max: 9, min: 1 }

Q3. Count Occurrences of Each Element [E][FB]


🧠 How to Approach This

Step 1 β€” Recognize the pattern:

Frequency counting = Map or plain object as hashmap. O(n) time.

Step 2 β€” Key insight:

For each element: if already in map, increment; otherwise initialize to 1. map.get(key) || 0 handles missing keys cleanly.

Step 3 β€” Follow-up questions to anticipate:

"Most frequent element" β†’ find entry with max value in map.

"Majority element (>n/2)" β†’ Boyer-Moore Voting Algorithm.

Step 4 β€” Edge cases:

- Mixed types: Map handles 1 and "1" as different keys (unlike plain objects)

Way of Thinking: Use a Map for frequency counting. Map preserves insertion order and handles any key type.

function countOccurrences(arr) {
  const freq = new Map();
  for (const item of arr) {
    freq.set(item, (freq.get(item) || 0) + 1);
  }
  return freq;
}

// Using reduce
function countOccurrencesReduce(arr) {
  return arr.reduce((acc, item) => {
    acc[item] = (acc[item] || 0) + 1;
    return acc;
  }, {});
}

console.log(countOccurrences([1,2,2,3,3,3]));
// Map { 1 => 1, 2 => 2, 3 => 3 }

Q4. Remove Duplicates [E][FB]


🧠 How to Approach This

Step 1 β€” Choose right approach for context:

- Unsorted, need new array: [...new Set(arr)] β€” O(n) time, O(n) space

- Sorted, in-place: two-pointer (slow/fast) β€” O(n) time, O(1) space

Step 2 β€” Set approach:

Set automatically deduplicates. Spread back into array to preserve array type.

Step 3 β€” Two-pointer in-place (sorted):

slow = write pointer, fast = scan pointer. Write nums[fast] when it differs from previous.

Step 4 β€” Edge cases:

- Empty array: return []

- All duplicates: result has one element

Way of Thinking: Set automatically stores unique values. Spread back into array.

// Method 1: Set (simplest)
function removeDuplicates(arr) {
  return [...new Set(arr)];
}

// Method 2: filter + indexOf
function removeDuplicates2(arr) {
  return arr.filter((item, index) => arr.indexOf(item) === index);
}

// Method 3: In-place for sorted array (two pointers)
function removeDuplicatesSorted(nums) {
  let k = 0;
  for (let i = 0; i < nums.length; i++) {
    if (i === 0 || nums[i] !== nums[i - 1]) {
      nums[k++] = nums[i];
    }
  }
  return k; // return new length
}

console.log(removeDuplicates([1,2,2,3,3,3,4])); // [1,2,3,4]

Q5. Check if Array is Sorted [E]


🧠 How to Approach This

Step 1 β€” Recognize the pattern:

Single-pass adjacent comparison. Return false on first violation.

Step 2 β€” Key insight:

Only need to compare arr[i] vs arr[i-1] (or arr[i+1]). If any adjacent pair is out of order, the whole array is not sorted. Short-circuit as soon as you find one violation.

Step 3 β€” Elegant JS:

arr.every((v, i) => i === 0 || arr[i-1] <= v) β€” concise and readable.

Step 4 β€” Edge cases:

- Empty or single element: always sorted

- Duplicate adjacent values: still sorted (equal is fine)

Way of Thinking: Compare each adjacent pair. If any pair is out of order, return false.

function isSorted(arr, ascending = true) {
  for (let i = 1; i < arr.length; i++) {
    if (ascending && arr[i] < arr[i-1]) return false;
    if (!ascending && arr[i] > arr[i-1]) return false;
  }
  return true;
}

// Using every (elegant)
const isSortedAsc = arr => arr.every((v, i) => i === 0 || arr[i-1] <= v);
const isSortedDesc = arr => arr.every((v, i) => i === 0 || arr[i-1] >= v);

console.log(isSorted([1,2,3,4,5]));       // true
console.log(isSorted([1,3,2,4,5]));       // false
console.log(isSorted([5,4,3,2], false));  // true

Q6. Rotate Array by K Positions [M][FB]


🧠 How to Approach This

Step 1 β€” Recognize the pattern:

Rotate right by k = last k elements move to front. "Reverse trick" for O(1) space.

Step 2 β€” Brute force:

Slice and concatenate: [...arr.slice(-k), ...arr.slice(0,-k)] β€” O(n) space, perfectly fine in JS interviews.

Step 3 β€” Optimal: reverse trick O(1) space:

1. Reverse all elements

2. Reverse first k elements

3. Reverse remaining elements

Result is rotated right by k.

Step 4 β€” Always do k = k % n first to handle k > n.

Step 5 β€” Edge cases:

- k = 0 or k = n: no change

- k > n: modulo handles it

Way of Thinking: Reverse trick: reverse entire array β†’ reverse first k β†’ reverse rest k..n. Handles k > n with modulo.

function rotate(nums, k) {
  k = k % nums.length; // handle k > length
  if (k === 0) return nums;

  function reverse(arr, left, right) {
    while (left < right) {
      [arr[left], arr[right]] = [arr[right], arr[left]];
      left++;
      right--;
    }
  }

  reverse(nums, 0, nums.length - 1); // reverse all
  reverse(nums, 0, k - 1);           // reverse first k
  reverse(nums, k, nums.length - 1); // reverse rest
  return nums;
}

// One-liner (uses extra space)
const rotateFn = (arr, k) => {
  k = k % arr.length;
  return [...arr.slice(-k), ...arr.slice(0, -k)];
};

console.log(rotate([1,2,3,4,5,6,7], 3)); // [5,6,7,1,2,3,4]