Finding text inside a string
Often you need to know where a piece of text sits inside a longer string, or whether it is there at all. TypeScript strings are JavaScript strings, so they come with the indexOf method for this:
s.indexOf(sub) // index of the first occurrence, or -1 if not found
s.lastIndexOf(sub) // index of the last occurrence, or -1
s.indexOf(sub, start) // start searching at index start
The result is a number. When the substring is found, it is the index of its first character. When it isn't found, the result is -1, which can never be a real index.
const sentence: string = "Hello World";
console.log(sentence.indexOf("World")); // 6
console.log(sentence.indexOf("o")); // 4
console.log(sentence.indexOf("xyz")); // -1
"World" starts at index 6 because H e l l o takes indexes 0 to 4, the space is index 5, and the W is index 6. The call sentence.indexOf("o") returns 4, the first o, even though a second one appears later.
Last occurrence and a starting point
lastIndexOf scans from the end and returns the position of the last match. The second argument of indexOf says where to begin, which lets you skip past a match you already have:
const sentence: string = "Hello World";
console.log(sentence.lastIndexOf("o")); // 7
console.log(sentence.indexOf("o", 5)); // 7
Calling indexOf("o", 5) ignores everything before index 5, so it skips the o in "Hello" and finds the one in "World". To visit every match, search again starting one past the previous result.
The exercise
The starting code defines the string:
const sentence: string = "Hello World";
The task is to store the index where "World" begins in a new variable named idx, with the type number.
- Write a
constdeclaration belowsentence, namedidx, with the annotation: number. - Call
indexOfonsentenceand pass"World". - Assign that result to
idx.
The finished code:
const sentence: string = "Hello World";
const idx: number = sentence.indexOf("World");
Here idx holds 6. The method only reads the string, so sentence itself doesn't change.
Gotchas
- The search is case-sensitive.
sentence.indexOf("world")returns-1, because the lowercasewdoesn't match the capital one. To ignore case, convert both sides withtoLowerCase()first. - Index 0 is a valid result, but it is falsy.
if (sentence.indexOf("Hello"))doesn't run its block, because the result is 0, which counts as false. Compare explicitly:sentence.indexOf("Hello") !== -1. Or useincludes, which answers a yes or no question:
const sentence: string = "Hello World";
console.log(sentence.indexOf("Hello") !== -1); // true
console.log(sentence.includes("Hello")); // true
console.log(sentence.includes("xyz")); // false
- Don't use
-1as an index. If the text may be missing, check the result before using it.sentence[sentence.indexOf("xyz")]readssentence[-1], which isundefined. - An empty substring matches immediately.
sentence.indexOf("")returns 0. - It finds text, not patterns. The argument is plain text. The method doesn't understand wildcards or regular expressions; for those,
searchormatchare the tools.
Putting indexOf to work
The result of indexOf is usually a stepping stone to something else, such as cutting a string at a known point. Here the position of @ splits an address into two parts:
const email: string = "ana@example.com";
const at: number = email.indexOf("@");
console.log(email.slice(0, at)); // ana
console.log(email.slice(at + 1)); // example.com
The start argument also lets you count every match. After each hit, search again from one position later, and stop when the result is -1:
const text: string = "one fish, two fish, red fish";
let pos: number = text.indexOf("fish");
let count: number = 0;
while (pos !== -1) {
count++;
pos = text.indexOf("fish", pos + 1);
}
console.log(count); // 3
The matches sit at indexes 4, 14 and 24. Without the + 1, the loop would find the same match again and never finish.
Choosing the right tool
Pick the method that matches the question you are asking. If you need the position, use indexOf. If you only need to know whether the text occurs, includes reads better and avoids the -1 comparison. If you need to know whether a string begins or ends with some text, startsWith and endsWith say so directly. All of them are case-sensitive and all of them leave the original string unchanged.
Key takeaways
indexOfreturns the index of the first match, or-1when there is none.lastIndexOfreturns the last match, and a second argument toindexOfsets where the search starts.- The search is case-sensitive, and the result
0is a real match. - Test with
!== -1or useincludeswhen you only need to know whether the text is present.