JavaScript Substring Method

JavaScript Substring Method

refine repo

Introduction

This post is about how to effectively use the String.prototype.substring() method in JavaScript. We go through a few examples to understand how it works, play around to observe a few patterns and explore the quirks.

JavaScript substring() is a String method that is typically used to extract and store part of a string. During the extraction, the original string remains intact, and the target part is returned as a new string.

String.prototype.substring() is handy for producing substrings of characters from a point to another of a parent string. The substring may be from the beginning, in-between specified indexes or from the tail.

In this post, we perform some exercise with the JS substring() method by going through a few examples. We start by getting familiar with the method signature of String.prototype.substring(), the required argument (startIndex), the additional argument (endIndex) and understand which ones to use when. We then go ahead and see examples of extracting a number of characters after a starting index, characters from a range between two indexes, characters from the beginning and from the tail. While doing so, we elaborate on a few patterns of extraction from a parent string: namely, that of extracting a tail after first n characters, another of producing a substring composed of first n characters and that of extracting last n characters.

In the later half of the post, we discuss some of the nuances associated with startIndex and endIndex values. In the end, we point out how JavaScript String.prototype.substring() compares to String.prototype.slice() as well as the deprecated method String.prototype.substr().

JavaScript substring() Method

Array.prototype.substring() takes two possible arguments: a startIndex and an endIndex. startIndex is mandatory, while endIndex can be passed to explicitly indicate the termination of extraction.

Array.prototype.substring() Method Signature

We can call substring()with the below possible signatures:

substring(startIndex);
substring(startIndex, endIndex);

startIndex represents the point where substring extraction kicks off. The value at startIndex is inclusive, i.e. it is included in the returned substring. The endIndex denotes termination of the extraction. However, the value at endIndex is excluded. This means that we end extraction at the character before endIndex.

Let's see this in action with some examples.

JavaScript substring() with startIndex Only

With only startIndex passed to substring(), we get a substring that begins at startIndex and spans to the end of the parent string:

const mnemonic = "Please, send, cats, monkeys, and, zebras, in, large, cages, make, sure, padlocked.";
console.log(mnemonic.substring(14)); // "cats, monkeys, and, zebras, in, large, cages, make, sure, padlocked."

As we can figure out from the string above, chopping starts at startIndex and we get the tail from that point on.

Extract Tail After First n Characters - JavaScript substring()

Since we are using a zero-based index for startIndex, the first startIndex number of characters are represented by the index just before startIndex. And since the value at startIndex is included in the extracted tail, the following pattern emerges where with startIndex = n, we get the tail after first n characters in the parent string:

const mnemonic = "Please, send, cats, monkeys, and, zebras, in, large, cages, make, sure, padlocked.";

// Extract characters after first 14
console.log(mnemonic.substring(14)); // "cats, monkeys, and, zebras, in, large, cages, make, sure, padlocked."

// Extract tail after first 20
console.log(mnemonic.substring(20)); // "monkeys, and, zebras, in, large, cages, make, sure, padlocked."

// Extract tails after 29
console.log(mnemonic.substring(29)); // "and, zebras, in, large, cages, make, sure, padlocked."

// Extract after 72
console.log(mnemonic.substring(72)); // "padlocked."

JavaScript String.prototype.substring() - Extract a Substring Between Two Points

When we pass both the startIndex and endIndex, we get a substring of characters in startIndex <= str < endIndex range:

const mnemonic = "Please, send, cats, monkeys, and, zebras, in, large, cages, make, sure, padlocked.";
console.log(mnemonic.substring(14, 27)); // "cats, monkeys"

This means, now we end up with a substring that ends before the endIndex.

Extract First n Characters - JavaScript substring()

When we need a substring from the start of the parent string, the startIndex should be 0:

const mnemonic = "Please, send, cats, monkeys, and, zebras, in, large, cages, make, sure, padlocked.";
console.log(mnemonic.substring(0, 27)); // "Please send, cats, monkeys"

With startIndex = 0 and endIndex = n, we get the first n characters from the parent string:

const mnemonic = "Please, send, cats, monkeys, and, zebras, in, large, cages, make, sure, padlocked.";

// First 12 characters
console.log(mnemonic.substring(0, 12)); // "Please, send"

// First 18 characters
console.log(mnemonic.substring(0, 18)); // "Please, send, cats"

// First 27 characters
console.log(mnemonic.substring(0, 27)); // "Please, send, cats, monkeys"

// First 70 characters
console.log(mnemonic.substring(0, 70)); // "Please, send, cats, monkeys, and, zebras, in, large, cages, make, sure"

Extract Last n Characters Using String.prototype.substring()

We can get the last n characters by leveraging the caller length in startIndex:

const mnemonic = "Please, send, cats, monkeys, and, zebras, in, large, cages, make, sure, padlocked.";

// Last 9 characters
console.log(mnemonic.substring(mnemonic.length - 9)); // "padlocked."

// Last 15 characters
console.log(mnemonic.substring(mnemonic.length - 15)); // "sure, padlocked."

The way the above pattern works is that mnemonic.length sets the startIndex just outside the parent string. Moving startIndex back by -n repositions the extraction entry to the tail by n. And since we are not passing endIndex, extraction continues till the end of the string. So, we get the last n characters.

Nuances of startIndex and endIndex in JavaScript substring()

Other quirks of using substring() include cases when startIndex is greater than endIndex and when either of startIndex and endIndex or both are negative. Let's look at them one by one.

JavaScript substring() with startIndex > endIndex

When startIndex is greater than endIndex JavaScript substring() swaps the indexes to produce the substring:

const mnemonic = "Please, send, cats, monkeys, and, zebras, in, large, cages, make, sure, padlocked.";

// Swaps to mnemonic.substring(0, 6)
console.log(mnemonic.substring(6, 0)); // "Please"

JavaScripty substring() with Negative startIndex and endIndex

A negative value of startIndex or endIndex sets the respective value to 0:

const mnemonic = "Please, send, cats, monkeys, and, zebras, in, large, cages, make, sure, padlocked.";

// Same as mnemonic.substring(0, 6)
console.log(mnemonic.substring(-1, 6)); // "Please"

// Same as mnemonic.substring(0, 0)
console.log(mnemonic.substring(-1, -6)); // ""

// // Same as mnemonic.substring(12, 0)
console.log(mnemonic.substring(12, -6)); // "Please, send"

In the last invocation above, swapping between startIndex and endIndex is also involved since 12 > 0. The call is equivalent to mnemonic.substring(0, 12);.

String.prototype.substring() Comparison

In this section, we briefly discuss how JavaScript substring() method differs from slice() and substr(), which are also similar Stringextraction methods.

JavaScript substring() vs slice()

String.prototype.substring() and String.prototype.slice() both implement string extraction almost identically. However, there are some subtle differences in their implementations.

For example, swapping of arguments -- which we saw above in substring() -- doesn't take place in slice() when startIndex > endIndex:

const mnemonic = "Please, send, cats, monkeys, and, zebras, in, large, cages, make, sure, padlocked.";

// Swaps to mnemonic.substring(0, 6)
console.log(mnemonic.substring(6, 0)); // "Please"

// Doesn't swap. Returns empty string, meaning non-existent characters
console.log(mnemonic.slice(6, 0)); // ""

With slice(), startIndex > endIndex returns an empty string.

Another difference is when either or both of startIndex and endIndex are negative. In general, when they are negative, slice() wraps startIndex and endIndex to the tail by traversing backward from the last character. And then if startIndex < endIndex, slicing works:

const mnemonic = "Please, send, cats, monkeys, and, zebras, in, large, cages, make, sure, padlocked.";

// Zeroes and swaps to mnemonic.substring(0, 18)
console.log(mnemonic.substring(18, -12)); // "Please, send, cats"

// Doesn't swap, -12 finds index from tail. String is long enough, slicing works
console.log(mnemonic.slice(20, -12)); // "monkeys, and, zebras, in, large, cages, make, sure"
.........

console.log(mnemonic.substring(-12, 18)); // "Please, send, cats"

// Doesn't swap. -12 finds index from tail, but startIndex > endIndex. Returns non-existent string
console.log(mnemonic.slice(-12, 18)); // ""
.........

console.log(mnemonic.substring(-16, -12)); // ""

// Both startIndex and endIndex finds index from tail. startIndex < endIndex. Slicing works
console.log(mnemonic.slice(-16, -12)); // "sure"

JavaScript substring() vs substr()

JavaScript substr() is also another method for extracting substrings from a parent string. However, it is a legacy String method that accepts the length of the target substring, instead of an index for extraction exit.

In other words, with substr(), we get indicate the length of the substring:

const mnemonic = "Please, send, cats, monkeys, and, zebras, in, large, cages, make, sure, padlocked.";

console.log(mnemonic.substring(10, 12)); // "nd"

// Gets 12 characters starting from startIndex onwards
console.log(mnemonic.substr(10, 12)); // "nd, cats, mo"

Also, as with slice(), with a negative startIndex, substr() counts backwards from the last character:

const mnemonic = "Please, send, cats, monkeys, and, zebras, in, large, cages, make, sure, padlocked.";

console.log(mnemonic.substring(-10, 12)); // "Please, send"

// startIndex wraps to 10 items from tail. Gets 10 characters from tail, doesn't have the other 2
console.log(mnemonic.substr(-10, 12)); // "padlocked."

In this case, only 10 characters are picked because 2 out of targeted 12 are not available in the last 10 characters.

Summary

In this post, we exercised the use of JavaScript substring() with some basic examples. We first learned that substring() is a String extraction method that returns a target portion from a parent string. We got familiar with the required startIndex argument whose value is included in the extracted substring, and also with the endIndex argument which indicates termination of the string.

We then explored with examples how to use substring() with only startIndex and discovered how to extract the tail after first n characters of the parent string. We also saw how to extract a substring between two points with startIndex and endIndex passed. We found out another pattern where we can produce the first n characters by passing 0 as startIndex and n as the endIndex. We also figured out how we can extract the last n characters using caller length - n.

Towards the later half, we touch based on some other nuances of using startIndex and endIndex values. In the end, we compared and discussed how substring() differs in implementation from JavaScript slice() and substr().