Course: JavaScript

Progress (0%)

  1. Foundation

  2. Numbers

  3. Strings

  4. Conditions

  5. Loops

  6. Arrays

  7. Functions

  8. Objects

  9. Exceptions

  10. HTML DOM

  11. CSSOM

  12. Events

  13. Drag and Drop

  14. opt Touch Events

  15. Misc

  16. Project: Analog Clock

Exercise: endsWith() Polyfill

Exercise 18 Easy

Prerequisites for the exercise

  1. JavaScript String Methods
  2. JavaScript String Basics
  3. All previous chapters

Objective

Create a polyfill function for the string method endsWith().

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')
true
'Hello'.endsWith('lo')
true
'Food.'.endsWith('d')
false

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)
false
'Hello'.endsWith('lo', 5)
true
'Food.'.endsWith('d', 4)
true

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.

View Solution

New file

Inside the directory you created for this course on JavaScript, create a new folder called Exercise-18-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.

If 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')
true
endsWithPolyfill('Hello', 'ell')
false
endsWithPolyfill('Hello', 'ell', 4)
true
endsWithPolyfill('Food.', '.')
true
endsWithPolyfill('Food.', 'd', 4)
true

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')
true
endsWithPolyfill('Hello', 'ell')
false
endsWithPolyfill('Hello', 'ell', 4)
true
endsWithPolyfill('Food.', '.')
true
endsWithPolyfill('Food.', 'd', 4)
true

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() implementationslice() 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.