String.prototype.count(searchString[, start[, end]])

Counting occurrences of a substring is a common operation in string processing. Currently, there is no built-in method in ECMAScript to do this.

Many other languages make this task straightforward:

  • Python: str.count(sub[, start[, end]])

  • PHP: substr_count()

  • Ruby: String#count

But in JavaScript, you have to rely on regex or split/join operations to achieve this.

Proposed Method Signature

String.prototype.count(searchString[, start[, end]])

Implementation

if (!String.prototype.count) {
  String.prototype.count = function (searchString, start, end) {
    if (this == null) {
      throw new TypeError("String.prototype.count called on null or undefined");
    }

    const string = String(this);
    const search = String(searchString);
    const startIndex = Math.max(0, Math.min(string.length, start ? Number(start) || 0 : 0));
    const endIndex = Math.max(startIndex, Math.min(string.length, end ? Number(end) || string.length : string.length));

    if (!search) return 0; 

    const substring = string.slice(startIndex, endIndex);
    let count = 0;
    let position = 0;

    while ((position = substring.indexOf(search, position)) !== -1) {
      count++;
      position += search.length;
    }

    return count;
  };
}

Example Usage

const str = "hello world, hello universe";

console.log(str.count("hello"));       // 2
console.log(str.count("o"));           // 3
console.log(str.count("hello", 13));   // 1
console.log(str.count("hello", 0, 5)); // 1
console.log(str.count("notfound"));    // 0
console.log(str.count(""));            // 0

Proposed Specification

  1. Let O be ? RequireObjectCoercible(this value).
  2. Let S be ? ToString(O).
  3. Let searchString be ? ToString(searchString).
  4. If start is undefined, let startIndex be 0.
  5. Else, let startIndex be ? ToIntegerOrInfinity(start).
  6. If end is not present, let endIndex be the length of S.
  7. Else, let endIndex be ? ToIntegerOrInfinity(end).
  8. Let startIndex be min(max(startIndex, 0), the length of S).
  9. Let endIndex be min(max(endIndex, 0), the length of S).
  10. If startIndex >= endIndex, return 0.
  11. Let subString be the substring of S from startIndex to endIndex.
  12. If searchString is an empty string, return 0.
  13. Let count be 0.
  14. Let position be 0.
  15. Repeat, while position is not -1:
    1. Let position be the result of searching for searchString in subString starting at index position.
    2. If position is not -1:
      1. Increment count by 1.
      2. Set position to position + the length of searchString.
  16. Return count.

Note: This method is intentionally generic; it does not require that its this value be a String object.
Therefore, it can be transferred to other kinds of objects for use as a method.

Behavior

  • It is case-sensitive, consistent with other string methods like includes().
  • Ensures that the method is called on a value that can be converted to an object (e.g., strings, numbers, or booleans). Throws a TypeError if the method is called on null or undefined.
  • If start is not provided, it defaults to 0. If provided, it is converted to an integer. The final value is clamped to be within [0, length of string] to ensure it is valid.
  • If end is not provided, it defaults to the length of the string. If provided, it is converted to an integer. The final value is clamped to be within [0, length of string] to ensure it is valid.
  • If startIndex is greater than or equal to endIndex, there is no range to search within, so the method immediately returns 0.
  • If searchString is an empty string, returns 0. This avoids potential infinite loops when counting empty substrings.

Q&A

Q: Why not add an option to toggle case sensitivity?

A: To maintain consistency with other string methods like includes().

GitHub

proposal-string-prototype-count

1 Like

What is the problem this is solving? Can you elaborate on "string processing"?

(note that a solution isn't important prior to stage 2, but a clear problem statement is)

Writing your own function takes more time especially when you need to handle start and end parameters. This is a common topic on Stack Overflow, and I think it's a task frequent enough to deserve its own dedicated method. Many programming languages already include functionality for counting substrings, so it feels like a logical and useful addition.

When I say text processing, I mean tasks like counting newline characters (\n) to figure out the number of lines in a text areaβ€”like when displaying line numbers in a code editor or counting how many times a keyword appears on a page to optimize content for SEO.

1 Like

So the problem to solve seems to be to count the occurrence of some substring.
Naive solutions may reach for .split(substr) or .matchAll(regex) and then use the .length to do the counting. Next choice would be a solution that uses indexOf and using start to repeatedly find the next occurrence, this at least avoids allocations.

But here the problem is restricted to substring matching only, not to support regexes.

1 Like