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!
Post a Comment