Logo

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:

  1. Looping with indexOf() or search() in a while loop until you no longer find matches.
  2. Regular expressions with match(), matchAll() (ES2020+), or a repeated exec() 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 where subStr is found (or -1 if none).
  • We increment count each time, then move position 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 shift position by 1 instead of subStr.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 of match.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() or indexOf(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

  1. Escape your Substring if it might contain special regex characters (like *, +, ., etc.). Otherwise, your pattern can break or match unintended strings.
  2. Overlapping vs Non-Overlapping: Clarify whether you need overlapping matches. The logic or method changes slightly.
  3. 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() or search(), 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:

  1. Grokking JavaScript Fundamentals
    Deepen your understanding of closures, prototypes, async/await, and more.

  2. 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.

CONTRIBUTOR
TechGrind