How can I count the number of times a particular string occurs in another string?
There are multiple ways to count occurrences of a substring within a larger string. Common approaches in JavaScript include:
- Looping with
indexOf()
orsearch()
in a while loop until you no longer find matches. - Regular expressions with
match()
,matchAll()
(ES2020+), or a repeatedexec()
loop to find multiple occurrences.
Below are a few detailed examples and considerations.
1. Looping with indexOf()
in JavaScript
function countOccurrences(str, subStr) { let count = 0; let position = 0; while (true) { // Find subStr starting from position const foundPos = str.indexOf(subStr, position); if (foundPos === -1) { break; // no more matches } count++; // Move position past the last found index position = foundPos + subStr.length; } return count; } console.log(countOccurrences("ababab", "ab")); // 3 console.log(countOccurrences("hello world", "o")); // 2
indexOf(subStr, fromIndex)
returns the index wheresubStr
is found (or-1
if none).- We increment
count
each time, then moveposition
forward past the substring to find subsequent matches. - This approach finds non-overlapping occurrences.
Overlapping Occurrences?
- If you need to count overlapping occurrences (e.g.,
"aaaa"
has 3 overlapping occurrences of"aa"
), you’d shiftposition
by 1 instead ofsubStr.length
:position = foundPos + 1;
2. Using a Regular Expression
2.1 String.prototype.match()
(Non-Overlapping)
function countOccurrencesRegex(str, subStr) { // Escape any special regex characters in subStr if needed: const escapedSubStr = subStr.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // Create a "global" RegExp const pattern = new RegExp(escapedSubStr, 'g'); // str.match(pattern) returns an array of matches, or null if none const matches = str.match(pattern); return matches ? matches.length : 0; } console.log(countOccurrencesRegex("ababab", "ab")); // 3 console.log(countOccurrencesRegex("hello world", "o")); // 2
- This also doesn’t handle overlapping matches by default because a global regex proceeds past the matched substring.
2.2 Counting Overlapping Occurrences with RegExp.prototype.exec()
Overlapping can be handled by manually iterating:
function countOverlapping(str, subStr) { // Escape if subStr might have special regex characters const escapedSubStr = subStr.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // 'g' for multiple matches; we manually manage lastIndex for overlaps const pattern = new RegExp(escapedSubStr, 'g'); let count = 0; let match; while ((match = pattern.exec(str)) !== null) { count++; // Manually reset the regex 'lastIndex' to allow overlapping pattern.lastIndex = match.index + 1; } return count; } console.log(countOverlapping("aaaa", "aa")); // 3
- After each match, we shift
pattern.lastIndex
by 1 (instead ofmatch.index + subStr.length
) so the next search can catch overlapping occurrences.
2.3 ES2020+ matchAll()
Modern JavaScript also has String.prototype.matchAll()
, which returns an iterator of match objects (including capturing groups). You can loop over them and count. However, handling overlaps still requires a workaround similar to the exec()
approach.
function countMatchesWithMatchAll(str, subStr) { const escapedSubStr = subStr.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); const pattern = new RegExp(escapedSubStr, 'g'); // matchAll returns an iterator of match results return [...str.matchAll(pattern)].length; } console.log(countMatchesWithMatchAll("ababab", "ab")); // 3
Still non-overlapping by default—overlapping requires manual resets, which typically means you go back to a custom loop.
3. Other Languages or Approaches
- Many languages (e.g., Python) have similar methods or libraries to count substrings.
- If you just need to see if a substring occurs at all,
String.includes()
orindexOf(subStr) !== -1
suffices (in JavaScript). - For large or complex tasks (like searching big logs or entire files), more advanced or memory-efficient solutions may be needed.
Best Practices
- Escape your Substring if it might contain special regex characters (like
*
,+
,.
, etc.). Otherwise, your pattern can break or match unintended strings. - Overlapping vs Non-Overlapping: Clarify whether you need overlapping matches. The logic or method changes slightly.
- Performance: For very large strings or repetitive tasks, consider efficiency. But for typical use cases, these examples are sufficient.
Final Thoughts
To count occurrences of a substring in JavaScript (or any language):
- Use a loop with
indexOf()
orsearch()
, adjusting the start index each time a match is found. - Or use regex (
match()
,exec()
,matchAll()
) with a global flag. - For overlapping occurrences, you must manually adjust how far the search index advances.
Bonus: Level Up Your JavaScript & Coding Interview Skills
If you’re exploring string manipulation and want to sharpen your JavaScript fundamentals or coding interview capabilities, check out these DesignGurus.io courses:
-
Grokking JavaScript Fundamentals
Deepen your understanding of closures, prototypes, async/await, and more. -
Grokking the Coding Interview: Patterns for Coding Questions
Master pattern-based problem-solving—useful for both interview prep and real-world dev tasks.
For hands-on feedback, explore Mock Interviews (Coding Mock Interview or System Design Mock Interview) with ex-FAANG engineers. Also, visit the DesignGurus.io YouTube channel for free tutorials on coding patterns, system design, and more.
Summary: Use a while loop with indexOf()
, or a regex approach (test()
, match()
, exec()
, matchAll()
) to count occurrences. Decide if you need overlapping matches and adjust your strategy accordingly.