Objective
Create a polyfill function for the string method endsWith()
.
Difficulty
Description
JavaScript provides a string method called endsWith()
that checks whether a string ends with a given substring, or not.
If it does, the method returns true
, however, if it doesn't, the method returns false
.
Consider the snippet below:
'Hello'.endsWith('o')
'Hello'.endsWith('lo')
'Food.'.endsWith('d')
In the first statement, since 'Hello'
ends with 'o'
, the method's call returns true
. So is the case with the second statement. In the third statement however, 'Food'
doesn't end with 'd'
, likewise we get false
returned.
The method could optionally be provided with a second argument, that is the length of the main string to consider.
Consider the snippet below:
'Hello'.endsWith('o', 2)
'Hello'.endsWith('lo', 5)
'Food.'.endsWith('d', 4)
In the first statement, the second argument 2
makes searching limited to the substring 'He'
. Since this (limited) string doesn't end with 'o'
, the method returns false.
In the second statement, the string considered in the searching is of length 5
i.e. the string 'Hello'
. Since this string ends with 'lo'
, we get true
returned.
In the third statement, the string considered in the searching is of length 4
i.e. the string 'Food'
. Since this ends with the character 'd'
, we get true
returned.
So this is how the method endsWith()
works.
Now the thing is that endsWith()
is not supported in earlier versions of Internet Explorer. This means that you'll have to create a polyfill for the method to use it in this browser.
In this exercise, you have to create a function endsWithPolyfill()
that works exactly like the string method endsWith()
as shown above.
Its syntax should be similar to the following:
function endsWithPolyfill(str, substr, length) {
// code here
}
str
should be the main string, substr
should be the string to search for in str
, and length
should be the length of str
to consider. It should default to the str.length
.
If str
(after considering length
) ends with substr
, the function should return true
; or otherwise false
.
Hints
Hint 1
One way is to use the lastIndexOf()
method to search for substr
in str
and check whether substr
ends at a position where the str
ends as well.
Hint 2
The other way is to use the slice()
method to extract out a substring from the end of str
which is as long as substr
, and see whether it is equivalent to substr
.
New file
Inside the directory you created for this course on JavaScript, create a new folder called Exercise-15-endsWith()-Polyfill and put the .html solution files for this exercise within it.
Solution
Let's understand how to check whether a given string str
ends with substr
, at a given length
.
First of all, we'll consider the simpler case — when length
is not given, in which case we assume that substr
has to come at the end of str
.
How to accomplish this simpler case?
Well, there are mainly two ways — one uses indexOf()
while the second one uses slice()
. We'll see the difference between using either of these once we create the complete endsWithPolyfill()
function.
Anyways, first let's consider the solution using indexOf()
.
Before we begin, it's useful to consider a few dummy arguments, see what to return in each case, and how to compute the return value. This planning is necessary before the actual coding.
Alright, time for some planning...
Suppose we have str
as 'Hello'
and substr
as 'o'
, given to endsWithPolyfill()
. The function should return true
in this case, since str
indeed ends with substr
.
Now, let's see how to programmatically compute this.
str.indexOf(substr)
returns 4
. Moreover, the last index of str
is also 4
. Likewise, we conclude that substr
occurs at the end of str
.
Time for another example.
Suppose we have str
as 'Hello'
and substr
as 'llo'
. The function should again return True
. str.indexOf(substr)
is 2
and the last index of str
is 4
.
Hmm, so the two values are different.
Remeber that str.indexOf()
just gives the index where a substring begins in a string. It doesn't give the index where it ends. But we could figure this out on our way using some elementary math.
substr
is 'llo'
and occurs at index 2
in str
, where would it end?Let's see it:
It would end at 4
.
Can we derive a formula for this?
Well, definitely yes!
The index where a given substring substr
ends in a string str
is equal to str.indexOf(substr) + substr.length - 1
.
In our case, if the ending index of substr
in str
is the same as the ending index of str
, we can conclude that str
ends with substr
.
This solves the simple case. Now, let's solve the case when length
is given.
Suppose that str
is 'Food'
, substr
is 'o'
and length
is 3
. The function should return true
since the constrained string, i.e. 'Foo'
, indeed ends with 'o'
.
In this case, str.indexOf(substr)
is 1
, likewise substr
ends at index 1
. However, the string str
ends at index 2
.
What's the problem here? Can you spot the cause of the problem?
The problem is caused by indexOf()
. It matches the first 'o'
in str
instead of the last one. How to solve this?
What we need to do is match the very last occurrence of substr
in str
. So, we could just use lastIndexOf()
.
Let's see what happens when we use lastIndexOf()
.
str.lastIndexOf(substr)
is 2
, which means that the index where this substr
ends is (2 + 1 - 1 =) 2
. The last index of str
is also 2
. Hence, we conclude that str
ends with substr
.
Great! This solves the second problem as well.
Below we create the implementation for endsWithPolyfill()
using lastIndexOf()
:
function endsWithPolyfill(str, substr, length) {
if (!length) length = str.length;
if (str.indexOf(substr) + substr.length - 1 === string.length - 1) return true;
else return false;
}
Note that the code above can be simplified to the following:
function endsWithPolyfill(str, substr, length) {
if (!length) length = str.length;
return str.indexOf(substr) + substr.length - 1 === string.length - 1;
}
With the function created, let's check it on given arguments:
endsWithPolyfill('Hello', 'o')
endsWithPolyfill('Hello', 'ell')
endsWithPolyfill('Hello', 'ell', 4)
endsWithPolyfill('Food.', '.')
endsWithPolyfill('Food.', 'd', 4)
A slice()
-based implementation
Another way to create a polyfill for endsWith()
is to use the string method slice()
.
Let's start by solving the simple case, that is when length
is not specified.
Suppose we have str
as 'Hello'
and substr
as 'o'
(and length
is not specified). The function should return true
since str
indeed ends with substr
.
But how to check this using slice()
?
Well, we slice a portion of the same length as substr
, from the end of str
, and see if it is equal to substr
. If it is, we conclude that str
ends with substr
.
In this case, the slice begins at index 4
and ends at 5
, giving the string 'o'
(the highlighted part in 'Hello'
). Since this slice is equivalent to substr
, we conclude that str
indeed ends with substr
.
Now suppose that str
is 'Hello'
and substr
is 'llo'
. The slice begins at index 2 and ends at index 5, giving the string 'llo'
. Once again, because this slice is equal to substr
, we return true
from the function.
The question is: how to know where to start the slicing?
The slice begins at str.length - substr.length
and goes upto str.length
. If this slice is equivalent to substr
, we return true
, or else false
.
Alright, time to solve the case when a length
argument is given.
Suppose that str
is 'Food'
, substr
is 'o'
and length
is 3
. Since the constrained string, i.e. 'Foo'
, indeed ends with 'o'
, the function should return true
.
Let's see what slicing gives us.
We begin slicing at length - substr.length
and go upto length
. In this case, this means that we begin at index 2
and go upto 3
, obtaining the string 'o'
(the highlighted part in 'Food'
).
Since this slice equals substr
, we conclude that yes, str
indeed ends with substr
.
Great! Both cases solved.
Below we create the implementation for endsWithPolyfill()
using slice()
:
function endsWithPolyfill(str, substr, length) {
if (!length) length = str.length;
if (str.slice(length - substring.length, length) === substr) return true;
else return false;
}
As before, this could be simplified to the following code:
function endsWithPolyfill(str, substr, length) {
if (!length) length = str.length;
return str.slice(length - substring.length, length) === substr;
}
endsWithPolyfill('Hello', 'o')
endsWithPolyfill('Hello', 'ell')
endsWithPolyfill('Hello', 'ell', 4)
endsWithPolyfill('Food.', '.')
endsWithPolyfill('Food.', 'd', 4)
Which implementation is better?
You may be thinking as to which implementation to use from these two — one based on lastIndexOf()
while the other based on slice()
.
Let's see it using a simple experiment.
We'll run each of these functions 10,000,000 times on the same input arguments with the help of a loop and then time the script on our machine. After 5 such trials, we get the following result.
lastIndexOf() implementation | slice() implementation |
---|---|
≈ 836 ms | ≈ 37 ms |
≈ 869 ms | ≈ 35 ms |
≈ 846 ms | ≈ 36 ms |
≈ 803 ms | ≈ 38 ms |
≈ 865 ms | ≈ 41 ms |
Notice how the implementation using slice()
is way more performant than the one using lastIndexOf()
, for exactly the same set of arguments.
So clearly, the slice()
-based implementation is the one you should use in your applications if performance really matters.
However, for small string processing concerns, using one or the other won't make any significant difference.