Episode - JavaScript Programs — Anagram, Palindrome, Reverse, Sort & More

Episode - JavaScript Programs — Anagram, Palindrome, Reverse, Sort & More

Hey everyone! Welcome back to Namaste JavaScript. Today we solve the most commonly asked JavaScript programming questions in interviews!

Each program is explained with multiple approaches — from brute force to optimal — with outputs and explanations!

What we will cover:

  • Anagram Check
  • Palindrome Check
  • String Reverse
  • Array Reverse
  • Array Sort
  • Find Largest Number from Array
  • Find Smallest Number from Array

1. Anagram Check

Two strings are anagrams if they contain the same characters in any order.

Examples:
  "listen" and "silent"   → Anagram ✅
  "triangle" and "integral" → Anagram ✅
  "hello" and "world"     → NOT Anagram ❌

Approach 1: Sort and Compare (Simple)

function isAnagram(str1, str2) {
    // Step 1: Remove spaces, lowercase both
    const clean = str => str.replace(/\s/g, "").toLowerCase();

    const a = clean(str1).split("").sort().join("");
    const b = clean(str2).split("").sort().join("");

    return a === b;
}

console.log(isAnagram("listen", "silent"));    // true
console.log(isAnagram("Triangle", "Integral")); // true
console.log(isAnagram("hello", "world"));      // false
console.log(isAnagram("Astronomer", "Moon starer")); // true

OUTPUT:
true
true
false
true

HOW IT WORKS:
"listen" → ["l","i","s","t","e","n"] → sort → ["e","i","l","n","s","t"] → "eilnst"
"silent" → ["s","i","l","e","n","t"] → sort → ["e","i","l","n","s","t"] → "eilnst"
"eilnst" === "eilnst" → true ✅

Time: O(n log n) — because of sorting
Space: O(n)

Approach 2: Character Frequency Map (Optimal)

function isAnagram(str1, str2) {
    const clean = str => str.replace(/\s/g, "").toLowerCase();
    const a = clean(str1);
    const b = clean(str2);

    // Different lengths → can't be anagram
    if (a.length !== b.length) return false;

    const charCount = {};

    // Count characters in first string
    for (const char of a) {
        charCount[char] = (charCount[char] || 0) + 1;
    }

    // Subtract characters from second string
    for (const char of b) {
        if (!charCount[char]) return false;  // char not found or count 0
        charCount[char]--;
    }

    return true;
}

console.log(isAnagram("listen", "silent"));  // true
console.log(isAnagram("hello", "world"));    // false

OUTPUT:
true
false

HOW IT WORKS:
charCount after first loop: { l:1, i:1, s:1, t:1, e:1, n:1 }
Second loop on "silent":
  s → count[s] = 0
  i → count[i] = 0
  l → count[l] = 0
  e → count[e] = 0
  n → count[n] = 0
  t → count[t] = 0
All counts went to 0 → they are anagrams!

Time: O(n) — two linear passes
Space: O(k) — k = number of unique characters
// One-liner using Map
function isAnagram(str1, str2) {
    const normalize = s => [...s.replace(/\s/g,"").toLowerCase()].sort().join("");
    return normalize(str1) === normalize(str2);
}

2. Palindrome Check

A string is a palindrome if it reads the same forwards and backwards.

Examples:
  "racecar"  → Palindrome ✅
  "madam"    → Palindrome ✅
  "A man a plan a canal Panama" → Palindrome ✅ (ignoring spaces/case)
  "hello"    → NOT Palindrome ❌

Approach 1: Reverse and Compare

function isPalindrome(str) {
    const clean = str.replace(/[^a-zA-Z0-9]/g, "").toLowerCase();
    const reversed = clean.split("").reverse().join("");
    return clean === reversed;
}

console.log(isPalindrome("racecar"));                     // true
console.log(isPalindrome("hello"));                       // false
console.log(isPalindrome("A man a plan a canal Panama")); // true
console.log(isPalindrome("madam"));                       // true
console.log(isPalindrome("12321"));                       // true

OUTPUT:
true
false
true
true
true

HOW IT WORKS:
"A man a plan a canal Panama"
  → remove non-alphanumeric → "AmanaplanacanalpanaMa"... wait:
  → lowercase → "amanaplanacanalpanama"
  → reversed  → "amanaplanacanalpanama"
  → same! ✅

Time: O(n)
Space: O(n) — for the reversed string

Approach 2: Two Pointer (Optimal — O(1) space)

function isPalindrome(str) {
    const clean = str.replace(/[^a-zA-Z0-9]/g, "").toLowerCase();

    let left = 0;
    let right = clean.length - 1;

    while (left < right) {
        if (clean[left] !== clean[right]) return false;
        left++;
        right--;
    }
    return true;
}

console.log(isPalindrome("racecar"));  // true
console.log(isPalindrome("hello"));    // false

OUTPUT:
true
false

HOW IT WORKS:
"racecar" → left=0(r), right=6(r) → match → left++, right--
            left=1(a), right=5(a) → match → left++, right--
            left=2(c), right=4(c) → match → left++, right--
            left=3(e), right=3(e) → left not < right → stop
All matched → palindrome! ✅

Time: O(n)
Space: O(1) — no extra string created (only two pointers)
// Check if a NUMBER is palindrome
function isNumPalindrome(num) {
    const str = String(Math.abs(num));  // Handle negative numbers
    return str === str.split("").reverse().join("");
}

console.log(isNumPalindrome(121));   // true
console.log(isNumPalindrome(-121));  // false (negative can't be palindrome)
console.log(isNumPalindrome(12321)); // true

3. String Reverse

Reverse a string — sounds simple, but interviewers want to see MULTIPLE approaches!

Input:  "Shreyesh"
Output: "hseyerhS"

Input:  "Hello World"
Output: "dlroW olleH"

Approach 1: Built-in Methods (Simplest)

function reverseString(str) {
    return str.split("").reverse().join("");
}

console.log(reverseString("Shreyesh"));    // hseyerhS
console.log(reverseString("Hello World")); // dlroW olleH

OUTPUT:
hseyerhS
dlroW olleH

HOW IT WORKS:
"Shreyesh"
  .split("")   → ["S","h","r","e","y","e","s","h"]
  .reverse()   → ["h","s","e","y","e","r","h","S"]
  .join("")    → "hseyerhS"

Time: O(n) | Space: O(n)

Approach 2: For Loop (Manual)

function reverseString(str) {
    let reversed = "";
    for (let i = str.length - 1; i >= 0; i--) {
        reversed += str[i];
    }
    return reversed;
}

console.log(reverseString("JavaScript")); // tpircSavaJ

OUTPUT:
tpircSavaJ

HOW IT WORKS:
Start from the LAST character and keep prepending to result.
"JavaScript" → j,t,p,i,r,c,S,a,v,a,J → "tpircSavaJ"

Approach 3: Two Pointer (In-place style)

function reverseString(str) {
    const arr = str.split("");  // Convert to array (strings are immutable)
    let left = 0;
    let right = arr.length - 1;

    while (left < right) {
        // Swap characters
        [arr[left], arr[right]] = [arr[right], arr[left]];
        left++;
        right--;
    }
    return arr.join("");
}

console.log(reverseString("Hello")); // olleH

OUTPUT:
olleH

HOW IT WORKS:
"Hello" → ["H","e","l","l","o"]
  swap(H,o) → ["o","e","l","l","H"]
  swap(e,l) → ["o","l","l","e","H"]
  left=2, right=2 → stop
  join → "olleh" ... wait:

Actually: ["H","e","l","l","o"]
  left=0(H), right=4(o) → swap → ["o","e","l","l","H"]
  left=1(e), right=3(l) → swap → ["o","l","l","e","H"]
  left=2, right=2 → stop → join → "olleH" ✅

Approach 4: Recursion

function reverseString(str) {
    if (str.length <= 1) return str;  // Base case
    return reverseString(str.slice(1)) + str[0];
}

console.log(reverseString("hello")); // olleh

OUTPUT:
olleh

HOW IT WORKS:
reverse("hello")
  = reverse("ello") + "h"
  = reverse("llo")  + "e" + "h"
  = reverse("lo")   + "l" + "e" + "h"
  = reverse("o")    + "l" + "l" + "e" + "h"
  = "o" + "l" + "l" + "e" + "h"
  = "olleh"

Time: O(n) | Space: O(n) stack frames

4. Array Reverse

Input:  [1, 2, 3, 4, 5]
Output: [5, 4, 3, 2, 1]

Approach 1: Built-in .reverse() — BUT it MUTATES!

const arr = [1, 2, 3, 4, 5];
arr.reverse();
console.log(arr);  // [5, 4, 3, 2, 1]

// ⚠️ WARNING: .reverse() mutates the ORIGINAL array!
const original = [1, 2, 3, 4, 5];
const reversed = original.reverse();
console.log(original);  // [5, 4, 3, 2, 1] ← CHANGED!
console.log(reversed);  // [5, 4, 3, 2, 1] ← same reference!

// ✅ If you want to preserve original:
const original = [1, 2, 3, 4, 5];
const reversed = [...original].reverse();  // spread to copy first!
console.log(original);  // [1, 2, 3, 4, 5] ← unchanged ✅
console.log(reversed);  // [5, 4, 3, 2, 1] ← reversed ✅

Approach 2: Two Pointer (In-place, no extra space)

function reverseArray(arr) {
    let left = 0;
    let right = arr.length - 1;

    while (left < right) {
        [arr[left], arr[right]] = [arr[right], arr[left]];  // Swap!
        left++;
        right--;
    }
    return arr;
}

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

OUTPUT:
[5, 4, 3, 2, 1]
[30, 20, 10]

Time: O(n) | Space: O(1) — in-place!

Approach 3: Using reduce (Functional Style)

function reverseArray(arr) {
    return arr.reduce((reversed, item) => [item, ...reversed], []);
}

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

HOW IT WORKS:
Start: []
  item=1 → [1, ...[]]  = [1]
  item=2 → [2, ...[1]] = [2, 1]
  item=3 → [3, ...[2,1]] = [3, 2, 1]
  item=4 → [4, 3, 2, 1]
  item=5 → [5, 4, 3, 2, 1]

Time: O(n) | Space: O(n)

5. Array Sort

JavaScript's built-in .sort() has a gotcha — you MUST know it!

Input:  [3, 1, 4, 1, 5, 9, 2, 6]
Output: [1, 1, 2, 3, 4, 5, 6, 9]  (ascending)

The Famous Gotcha — Default .sort() converts to STRING!

// ❌ BIG MISTAKE — default sort converts numbers to strings!
const nums = [10, 1, 21, 2, 100, 3];
console.log(nums.sort());

OUTPUT:
[1, 10, 100, 2, 21, 3]  ← WRONG! Sorted as strings!

// Why? "10" comes before "2" in string comparison!
// Because "1" < "2" alphabetically!

// ✅ CORRECT — provide a comparator function
console.log(nums.sort((a, b) => a - b));

OUTPUT:
[1, 2, 3, 10, 21, 100]  ← Correct! Sorted as numbers!

HOW comparator works:
  if (a - b) < 0 → a comes before b
  if (a - b) > 0 → b comes before a
  if (a - b) === 0 → order unchanged

Sort Ascending and Descending

const numbers = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3];

// Ascending
const asc = [...numbers].sort((a, b) => a - b);
console.log(asc);  // [1, 1, 2, 3, 3, 4, 5, 5, 6, 9]

// Descending
const desc = [...numbers].sort((a, b) => b - a);
console.log(desc);  // [9, 6, 5, 5, 4, 3, 3, 2, 1, 1]

OUTPUT:
[1, 1, 2, 3, 3, 4, 5, 5, 6, 9]
[9, 6, 5, 5, 4, 3, 3, 2, 1, 1]

Sort Array of Objects

const users = [
    { name: "Zara",     age: 28 },
    { name: "Arjun",    age: 22 },
    { name: "Priya",    age: 25 },
    { name: "Shreyesh", age: 25 }
];

// Sort by age ascending
users.sort((a, b) => a.age - b.age);
console.log(users.map(u => u.name + ":" + u.age));
// ["Arjun:22", "Priya:25", "Shreyesh:25", "Zara:28"]

// Sort by name alphabetically
users.sort((a, b) => a.name.localeCompare(b.name));
console.log(users.map(u => u.name));
// ["Arjun", "Priya", "Shreyesh", "Zara"]

// Sort by age, then by name (secondary sort)
users.sort((a, b) => a.age - b.age || a.name.localeCompare(b.name));

Bubble Sort — Manual Implementation

function bubbleSort(arr) {
    const result = [...arr];  // Don't mutate original
    const n = result.length;

    for (let i = 0; i < n - 1; i++) {
        for (let j = 0; j < n - i - 1; j++) {
            if (result[j] > result[j + 1]) {
                // Swap adjacent elements
                [result[j], result[j + 1]] = [result[j + 1], result[j]];
            }
        }
    }
    return result;
}

console.log(bubbleSort([64, 34, 25, 12, 22, 11, 90]));

OUTPUT:
[11, 12, 22, 25, 34, 64, 90]

Time: O(n²) | Space: O(n)
(Use built-in .sort() in real code — it's O(n log n)!)

6. Find Largest Number from Array

Input:  [3, 7, 1, 9, 4, 6, 2]
Output: 9

Approach 1: Math.max with Spread

function findLargest(arr) {
    return Math.max(...arr);
}

console.log(findLargest([3, 7, 1, 9, 4, 6, 2]));  // 9
console.log(findLargest([100, 200, 50, 75]));       // 200

OUTPUT:
9
200

CAUTION: Math.max(...arr) can fail for VERY large arrays!
(Stack overflow due to spread with 100,000+ elements)
For large arrays, use reduce instead!

Approach 2: reduce (Safe for Large Arrays)

function findLargest(arr) {
    return arr.reduce((max, current) => current > max ? current : max, arr[0]);
}

console.log(findLargest([3, 7, 1, 9, 4, 6, 2]));  // 9
console.log(findLargest([-5, -1, -10, -3]));       // -1 (all negatives!)

OUTPUT:
9
-1

HOW IT WORKS:
Start: max = 3
  current=7 → 7>3 → max=7
  current=1 → 1<7 → max=7
  current=9 → 9>7 → max=9
  current=4 → 4<9 → max=9
  current=6 → 6<9 → max=9
  current=2 → 2<9 → max=9
Result: 9 ✅

Time: O(n) | Space: O(1)

Approach 3: Manual Loop

function findLargest(arr) {
    if (arr.length === 0) return null;

    let largest = arr[0];

    for (let i = 1; i < arr.length; i++) {
        if (arr[i] > largest) {
            largest = arr[i];
        }
    }
    return largest;
}

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

OUTPUT:
9

Approach 4: Sort (Simple but O(n log n))

function findLargest(arr) {
    return [...arr].sort((a, b) => b - a)[0];  // Descending, take first
}

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

// Not ideal — O(n log n) when we only need O(n)
// But simple to remember!

7. Find Smallest Number from Array

Input:  [3, 7, 1, 9, 4, 6, 2]
Output: 1

Approach 1: Math.min with Spread

function findSmallest(arr) {
    return Math.min(...arr);
}

console.log(findSmallest([3, 7, 1, 9, 4, 6, 2]));  // 1
console.log(findSmallest([100, 200, 50, 75]));       // 50

OUTPUT:
1
50

Approach 2: reduce (Recommended)

function findSmallest(arr) {
    return arr.reduce((min, current) => current < min ? current : min, arr[0]);
}

console.log(findSmallest([3, 7, 1, 9, 4, 6, 2]));  // 1
console.log(findSmallest([-5, -1, -10, -3]));       // -10

OUTPUT:
1
-10

HOW IT WORKS:
Start: min = 3
  current=7 → 7>3 → min stays 3
  current=1 → 1<3 → min=1
  current=9 → 9>1 → min stays 1
  ... all others > 1
Result: 1 ✅

Approach 3: Manual Loop

function findSmallest(arr) {
    if (arr.length === 0) return null;

    let smallest = arr[0];

    for (let i = 1; i < arr.length; i++) {
        if (arr[i] < smallest) {
            smallest = arr[i];
        }
    }
    return smallest;
}

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

OUTPUT:
1

Bonus: Find BOTH Largest and Smallest in One Pass

function findMinMax(arr) {
    if (arr.length === 0) return { min: null, max: null };

    let min = arr[0];
    let max = arr[0];

    for (let i = 1; i < arr.length; i++) {
        if (arr[i] < min) min = arr[i];
        if (arr[i] > max) max = arr[i];
    }

    return { min, max };
}

console.log(findMinMax([3, 7, 1, 9, 4, 6, 2]));

OUTPUT:
{ min: 1, max: 9 }

Time: O(n) — only ONE loop for both!
This is BETTER than finding min and max separately (which would be 2 loops).
Interviewers love this answer!

Quick Recap — All Programs

Program Best Approach Time Space Key Trick
Anagram Frequency Map O(n) O(k) Count chars in str1, subtract in str2
Palindrome Two Pointers O(n) O(1) Compare left and right, move inward
String Reverse split().reverse().join() O(n) O(n) Strings are immutable — convert to array first
Array Reverse Two Pointers O(n) O(1) Swap left and right elements, move inward
Array Sort .sort((a,b) => a-b) O(n log n) O(log n) Always use comparator — default sort is string-based!
Find Largest reduce() O(n) O(1) Use reduce for large arrays (spread can overflow stack)
Find Smallest reduce() O(n) O(1) Same pattern as largest — just flip the comparison

Key Points to Remember

  • Anagram: sort both strings OR use frequency map — map is O(n), sorting is O(n log n)
  • Palindrome: two pointers from both ends is O(1) space — better than reversing
  • String reverse: strings are immutable in JS — must split into array first
  • Array .reverse() mutates the original — use [...arr].reverse() to preserve original
  • Array .sort() converts to strings by default — ALWAYS pass (a,b) => a-b for numbers
  • Math.max(...arr) can crash on very large arrays — use reduce for safety
  • Find both min AND max in one loop — interviewers love this optimization!
  • Always handle edge cases: empty array, all negatives, single element

Keep coding, keep learning! See you in the next one!