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:
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
- Let O be ? RequireObjectCoercible(this value).
- Let S be ? ToString(O).
- Let searchString be ? ToString(searchString).
- If start is undefined, let startIndex be 0.
- Else, let startIndex be ? ToIntegerOrInfinity(start).
- If end is not present, let endIndex be the length of S.
- Else, let endIndex be ? ToIntegerOrInfinity(end).
- Let startIndex be min(max(startIndex, 0), the length of S).
- Let endIndex be min(max(endIndex, 0), the length of S).
- If startIndex >= endIndex, return 0.
- Let subString be the substring of S from startIndex to endIndex.
- If searchString is an empty string, return 0.
- Let count be 0.
- Let position be 0.
- Repeat, while position is not -1:
- Let position be the result of searching for searchString in subString starting at index position.
- If position is not -1:
- Increment count by 1.
- Set position to position + the length of searchString.
- 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 onnull
orundefined
. - If
start
is not provided, it defaults to0
. 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 toendIndex
, there is no range to search within, so the method immediately returns0
. - If
searchString
is an empty string, returns0
. 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()
.