JavaScript Slice Method in Depth

Necati Özmen - Oct 17 '22 - - Dev Community

Author: Abdullah Numan

Introduction

This is a series on sparsely used iteration methods in JavaScript. In this series, we cover several methods that are used to iterate over a collection of data, act on them or produce side effects. These operations, especially with arrays and strings, are very common in front end development with libraries like React and Angular.

What is Iteration?

Iteration is the process of looping through a collection of data and acting on each item in order to manipulate the item or create a side effect from it. Common examples of iteration methods in JavaScript are Array.prototype.forEach(), Array.prototype.map() and Array.prototype.reduce().

There are other iteration methods which are not as often used, but when needed are always handy tools for iterating over arrays, strings and objects. slice(), some() and every() are such examples.

Some iteration methods, like Array.prototype.forEach() iterates over all items in a collection and some like Array.prototype.slice() iterates over the array partially.

We will cover several of these methods in this series, each in their own post.

In this article, we look into the details of using JavaScript slice which is available with identical implementations in Array and String prototypes. We'll get into their nuances with examples of both of these types.

Let's start with the array version of Javascript slice().

JS slice - Array.prototype.slice()

Array.prototype.slice() is an Array method that is used to extract a contiguous portion from an existing array. It can take two arguments: the start and the end indicator of the slice -- both of which are optional:

// Method signature

slice();
slice(start);
slice(start, end);
Enter fullscreen mode Exit fullscreen mode

In this section, we set off with an example that expounds some general cases for selecting and storing a section from a source array. Then we'll see a couple of more interesting examples that help us generate arrays from arguments passed to a function using Array.prototype.call() and Array.prototype.bind().

Slicing an Array

A typical examples of slicing an array involves extracting a contiguous part from an existing array. Such as the first three items, last three items and some items spanning from a certain index up until another index, as shown in the examples below:

const elements = ['Please', 'Send', 'Cats', 'Monkeys', 'And', 'Zebras', 'In', 'Large', 'Cages', 'Make', 'Sure', 'Padlocked'];

const firstThree = elements.slice(0, 3);
const lastThree = elements.slice(-3, elements.length);
const fromThirdToFifth = elements.slice(2, 5);
Enter fullscreen mode Exit fullscreen mode

Both arguments of slice() stand for zero-based index numbers or negative offset values. The first one (0 in the firstThree assignment above) represents the starting index or offset in the source array where slicing should begin and the second one (3) is the index or offset before which extraction should stop.

If we log the extracted values above, we can see the three elements we want from each slice:

console.log(firstThree); // ["Please", "Send", "Cats"]
console.log(lastThree); // ["Make", "Sure", "Padlocked"]
console.log(fromThirdToFifth); // ["Cats", "Monkeys", "And"]
Enter fullscreen mode Exit fullscreen mode

It is important to notice that the item represented by the second argument is excluded from the extracted part. And we should be careful that unlike in Array.prototype.splice(), whose second argument is the count of items to be deleted, the second argument of Array.prototype.slice() does not stand for the number of items to be extracted, rather the position before which slicing should stop.

Other Nuances

No Arguments

If we don't pass in any argument, we just get a shallow copy of the source array with all items:

const allCopied = elements.slice();

console.log(allCopied);
// (12) ["Please", "Send", "Cats", "Monkeys", "And", "Zebras", "In", "Large", "Cages", "Make", "Sure", "Padlocked"]
Enter fullscreen mode Exit fullscreen mode

It's effectively [...elements].

No Second Argument

If we don't pass in the second argument, the JavaScript slice extends to the last element:

const fromThird = elements.slice(2);
const lastThree = elements.slice(-3, elements.length);
const lastThreeWithNoSecArg = elements.slice(-3);

console.log(fromThird);
// (10) ["Cats", "Monkeys", "And", "Zebras", "In", "Large", "Cages", "Make", "Sure", "Padlocked"]

console.log(lastThree); // (3) ["Make", "Sure", "Padlocked"]
console.log(lastThreeWithNoSecArg); // (3) ["Make", "Sure", "Padlocked"]
Enter fullscreen mode Exit fullscreen mode

Do notice that lastThreeWithNoSecArg evaluates to the same portion as lastThree, albeit missing the second argument.


discord banner

Negative Offsets

As we've already seen above, we can pass in as arguments negative numbers that denote offset positions counted backwards from the last item. We can do this for both arguments:

const latestTwoBeforeLast = elements.slice(-3, -1);
console.log(latestTwoBeforeLast); // (2) ["Make", "Sure"]
Enter fullscreen mode Exit fullscreen mode

Here, with latestTwoBeforeLast, we're getting the latest two items without the final one.

Starting Position Greater Than Ending Position

If we pass in a greater value for start than end, we get an empty array:

const somewhereWeDontKnow = elements.slice(5, 2);
console.log(somewhereWeDontKnow); // []
Enter fullscreen mode Exit fullscreen mode

Starting Position Greater Than Length of Array

Likewise, if we pass in a greater value for start than array's length, we get an empty array:

const somewhereInOuterSpace = elements.slice(15, 2);
console.log(somewhereInOuterSpace); // []
Enter fullscreen mode Exit fullscreen mode

Sparse Arrays

If we our target portion has sparse items, they are also copied over:

const elements = ['Please', 'Send', , 'Cats', , 'Monkeys', 'And', 'Zebras', 'In', 'Large', 'Cages', 'Make', 'Sure', 'Padlocked'];

const sparseItems = elements.slice(0, 6);

console.log(sparseItems);
// (6) [ 'Please', 'Send', <1 empty item>, 'Cats', <1 empty item>, 'Monkeys' ]
Enter fullscreen mode Exit fullscreen mode

Creating Arrays from a List of Arguments

Now we can go a bit crazy about slicing. Let's construct an array from a list of arguments passed to a function:

const createArray = (...args) => Array.prototype.slice.call(args);
const array = createArray(1, 2, 3, 4);
console.log(array); // (4) [1, 2, 3, 4]
Enter fullscreen mode Exit fullscreen mode

Here, we received args as a list first, but converted it to an array with rest params ...args. We then bound the array to Array.prototype.slice() with Function.prototype.call().

That was easy.

The next level way of doing this is in the messiest possible way:

const boundSlice = Function.prototype.call.bind(Array.prototype.slice);

const createArray = (...args) => boundSlice(args);

const array = createArray(1, 2, 3, 4);
console.log(array); // (4) [1, 2, 3, 4]
Enter fullscreen mode Exit fullscreen mode

It seems like a overhead, but what were are doing is declaring two helper functions.

The first one, boundSlice, is derived from binding the Function.prototype.call() method with Array.prototype.slice() which is an array function object. So, we are getting a copy of Function.prototype.call() bound to Array.prototype.slice() and storing it in boundSlice.

This basically means, if we invoke boundSlice(), we are in effect invoking Array.prototype.slice.call(). If we then pass in an argument list as rest params to boundSlice(), Array.prototype.slice() is invoked on this array and a copy of the array is created from it.

In createArray(), we are accumulating the arguments and passing them to boundSlice() as args. So, whatever we pass to createArray() is returned as items inside an array.

JS slice - String.prototype.slice()

Now, let's consider the JavaScript slice() method for strings.

String.prototype.slice() does the exact same thing as Array.prototype.slice(), but with characters in a string.

Like Array.prototype.slice(), it takes two optional arguments, start and end:

// Method signature

slice();
slice(start);
slice(end);
Enter fullscreen mode Exit fullscreen mode

Slicing a String

String.prototype.slice() is useful for directly working on strings. It removes the hassle of converting a string to an array with Array.prototype.split():

const mnemonic = 'Please Send Cats Monkeys And Zebras In Large Cages Make Sure Padlocked';

const firstThreeChars = mnemonic.slice(0, 3);
const lastThreeChars = mnemonic.slice(-3, mnemonic.length);
const fromThirdToFifthChars = mnemonic.slice(2, 5);

console.log(firstThreeChars); // "Ple"
console.log(lastThreeChars); // "ked"
console.log(fromThirdToFifthChars); // "eas"
Enter fullscreen mode Exit fullscreen mode

Again, both arguments represent zero-based index numbers or negative offset values. Here, the first one, 0 in the firstThree assignment, stands for the starting index or offset of the portion and the second one (3) for the index or offset before which extraction should stop.

With No Arguments

If we don't pass in any arguments, we get a new copy of the whole string:

const mnemonic = 'Please Send Cats Monkeys And Zebras In Large Cages Make Sure Padlocked';
const memorizedMnemonic = mnemonic.slice();

console.log(memorizedMnemonic);
// "Please Send Cats Monkeys And Zebras In Large Cages Make Sure Padlocked"
Enter fullscreen mode Exit fullscreen mode

With No Second Argument

If we don't pass the second argument, the length of the string becomes end:

const charsFromThird = mnemonic.slice(2);

console.log(charsFromThird);
// "ease Send Cats Monkeys And Zebras In Large Cages Make Sure Padlocked"
Enter fullscreen mode Exit fullscreen mode

Negative Offsets

Similar to Array.prototype.slice(), negative values for start and end represent offset positions from the end of the array:

const lastThreeChars = mnemonic.slice(-3);
const latestTwoCharsBeforeLast = mnemonic.slice(-3, -1);

console.log(lastThreeChars); // "ked"
console.log(latestTwoCharsBeforeLast); // "ke"
Enter fullscreen mode Exit fullscreen mode

Starting Position Greater Than Ending Position or Length of Array

Again, similar to Array.prototype.slice(), if start is greater than end or length of string, we get an empty string:

const inAnotherDimension = mnemonic.slice(5, 2);
const doingStringTheory = mnemonic.slice(15, 2);

console.log(inAnotherDimension); // ""
console.log(doingStringTheory); // ""
Enter fullscreen mode Exit fullscreen mode

Is your CRUD app overloaded with technical debt?

Meet the headless, React-based solution to build sleek CRUD applications. With refine, you can be confident that your codebase will always stay clean and boilerplate-free.

Try refine to rapidly build your next CRUD project, whether it's an admin panel, dashboard, internal tool or storefront.


refine blog logo


Conclusion

In this post, we expounded the slice() method in JavaScript. We saw that JavaScript implements slice() in two flavors: one for Arrays with Array.prototype.slice() and one for Strings with String.prototype.slice(). We found out through examples that both methods are used to extract contiguous portions from a source object representing these types.

We also saw that String.prototype.slice() is identical implementation of Array.prototype.slice() that removes the overhead of converting a string to an array of characters.

We also covered how function composition and context binding with Function.prototype.call() and Function.prototype.bind() allows us to define custom functions that help us generate arrays from a list of arguments.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .