-
Section 1: Getting Started
-
Section 2: Core Syntax and Types
-
Section 3: Strings and Numbers in Depth
-
Section 4: Control Flow
-
Section 5: Functions
-
Section 6: Objects and Arrays
-
Section 7: Maps, Sets, and Symbols
-
Section 8: Asynchronous JavaScript
-
Section 9: Object-Oriented and Prototypes
-
Section 10: The DOM
-
Section 11: Browser APIs
-
Section 12: Modern JavaScript (ES2015-ES2025)
-
Section 13: Functional Programming Patterns
-
Section 14: Error Handling and Debugging
-
Section 15: Testing
-
Section 16: Accessibility for JavaScript Developers
-
Section 17: Internationalization and Localization
-
Section 18: Performance
-
Section 19: Node.js Fundamentals
-
Section 20: Regular Expressions
-
Section 21: Design Patterns in JavaScript
-
Section 22: Security Basics
-
Section 23: Data Structures and Algorithms in JavaScript
-
Section 24: Practical Projects
-
Section 25: More Advanced Async Patterns
-
Section 26: More Object and Class Practice
-
Section 27: Working with Dates and Internationalization
-
Section 28: Web Components
-
Section 29: More DOM and Browser Practice
-
Section 30: Build Tooling for Vanilla JavaScript
-
Section 31: More Practice Projects
-
Section 32: Interview and Algorithm Practice
-
Section 33: Error Objects (MDN Reference)
-
Section 34: TypedArrays and Binary Data
-
Section 35: Reflection and Metaprogramming (MDN Reference)
-
Section 36: More Global Functions (MDN Reference)
232: Solving Common Coding Challenges in JavaScript
You've probably seen those technical interview sites where you're asked to solve a riddle-like coding challenge in 30 minutes. They can feel intimidating, but once you've done a few, you realize they usually boil down to a handful of patterns: frequency maps, two-pointer movements, or sliding windows. Today, let's walk through one of the absolute classics—checking if two strings are anagrams—and I'll show you how I move from a "quick and dirty" solution to something production-ready.
The "Sort and Compare" Shortcut
When I first see an anagram challenge, my brain immediately goes to the simplest possible logic: if two words have the exact same letters in the exact same quantities, they'll look identical if I alphabetize them. It's not the most performant way to do it, but it's the fastest to write.
function isAnagram(str1, str2) {
const sorted1 = str1.split('').sort().join('');
const sorted2 = str2.split('').sort().join('');
return sorted1 === sorted2;
}
This is clean. I'm splitting the string into an array, sorting it, and smashing it back into a string for a direct comparison. In a rush, this is often all you need.
Wait, Case Sensitivity Matters
Here is where I usually trip up if I'm rushing. I'll run a test case like isAnagram('Listen', 'Silent') and—surprise—it returns false. Why? Because in JavaScript, 'L' and 'l' are completely different characters.
I forgot to normalize the input. In a real-world scenario, the user doesn't care about capitalization when they're looking for an anagram. I need to force everything to lowercase before I start the sorting process. Let's fix that:
function isAnagram(str1, str2) {
// Normalizing to lowercase so 'Listen' matches 'Silent'
const clean1 = str1.toLowerCase().split('').sort().join('');
const clean2 = str2.toLowerCase().split('').sort().join('');
return clean1 === clean2;
}
Now it works. But if you're interviewing for a senior role or dealing with massive datasets, a reviewer is going to ask you about "time complexity." Sorting takes $O(n \log n)$ time. If we're processing a million-character string, that's a lot of unnecessary work.
Scaling Up with a Frequency Map
To make this faster, I'll use a frequency map. Instead of sorting, I'll just count how many times each letter appears. If the counts match, it's an anagram. This brings our time complexity down to $O(n)$, which is as fast as it gets because we have to look at every letter at least once.
function isAnagram(str1, str2) {
const s1 = str1.toLowerCase();
const s2 = str2.toLowerCase();
if (s1.length !== s2.length) return false;
const charCount = {};
// Increment counts for the first string
for (let char of s1) {
charCount[char] = (charCount[char] || 0) + 1;
}
// Decrement counts for the second string
for (let char of s2) {
if (!charCount[char]) {
// If the char doesn't exist or count is 0, it's not an anagram
return false;
}
charCount[char]--;
}
return true;
}
I love this approach because it's robust. I'm using a single object to track the "balance" of characters. I add for the first word and subtract for the second. If I ever hit a character that isn't in my map, or if a count drops below zero, I can stop immediately and return false. It's efficient, it handles the edge cases, and it shows you're thinking about performance.
📋 Practical Task
Exercise: Finding the First Unique Character in a String
Using the "Frequency Map" pattern we just discussed, write a function called findFirstUniqueChar(str). The function should take a string and return the first character that does not repeat anywhere else in the string. If all characters repeat, return null.
Example requirements:
findFirstUniqueChar("javascript")should return"j".findFirstUniqueChar("stress")should return"t"(since 's' repeats).findFirstUniqueChar("aabbcc")should returnnull.
Hint: You'll need two loops. The first to build your frequency map, and the second to check the original string in order to find which character has a count of exactly 1.
There are no comments for now.