Exploring the hidden potential of JavaScript arrays!
Undoubtedly, arrays are one of the most useful and extremely popular JS data structures. These indexed collections proved time and time again that they’re still the best (if not only) choice for a various number of use cases. But, arrays themselves wouldn’t be so valuable without their whole, extremely expressive API. And that’s the topic of today’s post! 😀
We’ll go over some lesser-known and other possibly hard to remember methods that are included natively within arrays API. Some dating back surprisingly quite much! With their help, you’ll be able to write cleaner, more functional (in FP-style also) and sometimes even more performant JS code! Some of them might be easier, some harder, but this article is definitely not aimed at total beginners! Of course, if you already know and remember all of them, consider at least taking a while to remind yourself some details or learn some new tricks! ✨
Transformation
For better reading experience, I divided all listed methods into 4 separate groups. Each one collects methods sharing at least one special property. The first group is called “Transformation”. Here, all methods that transform the array into other forms find their place. All of them work in an immutable manner, returning new array in the result, without affecting the base one.
.filter()
I think I don’t have to talk about how useful filtering arrays can be. And, with the .filter()
you can do just that! All you have to do is pass a filtering function, which, given current element’s value, its index and source array, should output boolean value, indicating if the given element should or shouldn’t be included in the resulting array. 📋
const arr = [1,2,3,4];
const evenArr = arr.filter(num => num % 2 === 0); // [2,4]
.map()
.map()
is possibly one of the most beloved array methods in FP-tailored JS programming. As you may already know, it processes (“maps”) your array and, with the given mapping function, returns new one with freshly processed data. The mentioned function provides a standard parameters set of element, its index and source array parameters and should return value that’s meant to be included in the result. So, with all that data, you should have everything you need to change your array the way you want! 😁
const arr = [1,2,3,4];
const oneMoreArr = arr.map(num => num + 1); // [2,3,4,5]
.flat[Map]()
Nested arrays are quite common in modern days. They prove especially useful when representing 2D or 3D data formats. It’s totally possible to go even deeper with such dimensions, but, as we all know, it later becomes only harder and harder to keep track of and access such data. Guys behind ECMAScript specification development clearly recognized this pattern, and, beginning with latest ES specs and always-green browsers, introduced new .flat()
method to us. Its rules are simple - it simply flattens your nested array by specified depth (defaults to 1), effectively leaving you with an array more flatten than ever !
const arr = [1,[2,[3,[4]]]];
const flatten = arr.flat(3); // [1,2,3,4]
There’s yet another array-flattening-related method. I’m talking about [.flatMap()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flatMap)
and, as you might expect, it’s a perfect combination of .map()
and .flat()
. Basically, you can use this method just like .map()
- with the same set of parameters and etc., but the resulting array is later flattened 1 level deep. Simple. So, what are the possible use-cases of such method? For that, consider the example of string processing below.
let arr = ["This is", "an", "example"];
const mappedArr = arr.map(x => x.split(" "));
// [["This","is"],"an","example"]
const flatMappedArr = arr.flatMap(x => x.split(" "));
// ["This","is","an","example"]
Maybe this is a bit sketchy, but I think you get the point. As soon as you understand how this method is working, you surely will find some use-cases of your own. As a side-note, this way is a bit more performant ⚡ than using .map()
and .flat()
separately.
Interaction
“Interaction” category groups all methods that work on the source arrays and, instead of providing an entirely new one, alter the them or return completely different types of values. 🤯
.reverse()
Certainly simple, but a bit lesser-known method, .reverse()
does exactly what its name implies - reverses the ordering of elements inside your array. ➰ So the last shall be first. This will most likely come in handy when working with queues of different kinds. Remember that this method mutates the source array.
const arr = [1,2,3,4];
arr.reverse(); // [4,3,2,1]
.reduce[Right]()
If you want to quickly convert (“reduce”) your array to a single value, you can freely do it with the .reduce()
method. Provided the right function (so-called reducer), it will later execute it on every single element of an array and accumulate the result to a single variable.
const arr = [1,2,3,4];
const sum = arr.reduce((sum, num) => sum + num); // 10
The argument function should return the accumulated value, which can be later referenced back with its first argument. At its peak, the function can provide you with 4 parameters (in the given order):
- accumulated value (can be a string, array, object or whatever…);
- the current value of array being reduced;
- index of current value;
- array being reduced;
If reduction order is important to you, you should know that you also have accessed to [.reduceRight()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/ReduceRight)
which does exactly the same thing as the previous method, but beginning from the right side, going backward. 🔙
.find[Index]()
Finding a particular element in the array can be a tough task unless it’s the first or the last one of course. Here, added in ES6 .find()
method can be really helpful. It simply takes the checking function which handles the standard set of parameters, and returns the first matched value from the given array, undefined
otherwise.
const arr = [1,2,3,4];
const matched = arr.find(num => num % 2 === 0); // 2
There’s also an [.findIndex()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findIndex)
method which, just like the first one, uses the matching function to find the value, but instead returns its index instead of raw value. It can be compared to [.indexOf()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf)
or [.lastIndexOf()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/lastIndexOf)
, that also can be used to retrieve the index of the first and last value that matches the provided one, but it’s not as expressive as .findIndex()
with its matching function can be. 😑
const arr = [1,2,3,4];
const matched = arr.findIndex(num => num % 2 === 0); // 1
One last note though - if you’re using .indexOf()
only to check if a value exists in the given array, consider using [.includes()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes)
(ES6 feature) - it returns a boolean and have a bit better performance than its alternatives.
.every()
As much as some might expect .every()
to simply run provided function on every element of the given array, the naming can be misleading here. Instead, .every()
indeed runs a function on every element, but only to check if they follow provided guidelines and, in the end, return a rightful boolean value. Checking function provides the standard set of parameters. 👌
const arr = [1,2,3,4];
const isIncremental = arr.every((num, idx, arr) => {
const previousIdx = idx - 1;
if(previousIdx >= 0){
return num === arr[previousIdx] + 1
} else {
return true;
}
}); // true
.copyWithin()
Copying data within the margins of a single array can feel a bit complicated and pointless for some. But, with its superior performance 🌠 (especially in its TypedArrays counterpart), this method provides a great way to shift array elements fast! Here, you can pass 1 up to 3 arguments:
- the target index from which the copied data will be pasted. As
.copyWithin()
doesn’t change the length of source array, elements will be replaced and old data removed. - the starting index marking the beginning of data to be copied (defaults to 0 - array’s beginning)
- the ending index marking the end (excluding the provided index) of data to be copied (defaults to
.length
- the end of the given array)
const arr = [1,2,3,4];
arr.copyWithin(0,2); // Array.copyWithin() mutates the source array
arr; // [3,4,3,4]
.sort()
.sort()
is one of these methods that clearly does exactly what the name implies. In this case - it just sorts your array. It could be with or without a comparison function provided. By default, all values are converted to strings and sorted increasingly by UTF-16 code 🌐 values, meaning numbers from smaller to bigger and strings alphabetically. You can also provide a function that receives two elements for comparison as separate parameters. This comparison function should return a number, which will then be used to sort provided values in a given way:
- if the function returns number less than 0, the value provided as the first parameter comes first;
- if the function returns number equal to 0, the values will remain unchanged (not really guaranteed by the specification);
- if the function returns number higher than 0, the value provided as the second parameter comes first;
const arr = [1,2,3,4];
arr.sort((num1, num2) => {
return num2 - num1;
});
// Array.sort() mutates the source array
.some()
.some()
is a method similar to .every()
. It checks if elements inside the source array satisfy certain rules (provided in the form of checking function) and, in the end, returns a boolean value. The difference is that .some()
requires only one element ☝ to satisfy the test, to return a positive value, unlike .every()
which requires every element. It can help you, e.g. check if there’s at least one value with given properties. Provided test function receives a standard set of parameters (element, its index and source array)
const arr = [1,2,3,4];
arr.some((num) => {
return num % 5 === 0;
}); // false
Iteration
Surprise, surprise! 😲 There’s, in fact, only one method in array API to perform iteration only! Just a shout-out to those who use .map()
, .reduce()
and similar method, only to iterate over source arrays. There’s only one method for this task and thus, it should be respected and well-known.
.forEach()
.forEach()
, does what its name stands for - iterates over each element of the source array. Provided a function that receives standard arguments set, it runs it on every element of the given array. 🔁
const arr = [1,2,3,4];
arr.forEach((num) => {
console.log(num); // 1/2/3/4
})
Miscellaneous
Beyond all of the above categories, arrays API have some more methods in its stock. Below are some of them, that are surely lesser-known and used, but may come in handy in some special cases.
.entries()
.entries()
is the first of 3 iterator-object-returning methods. The array iterator object or so-called iterable is a simple structure that can be, naturally, iterated through using for... of
loop and has single .next()
method which is called under-the-hood when iterating. When called directly, it returns an object containing value
and done
properties, which, respectively, indicate current value and if iteration is complete. ⏳ When called again, it will return the next value from the array, and the process will continue until the end of the source array when the done
property will be set to true.
Our iterator returned by .entries()
method will have its values in the form of key-value pairs (arrays), where the first element indicates the current index and second - respective value. You can compare it (and other methods will talk about later) to their object counterparts. Functions like Object.entries()
or Object.keys()
(not on the prototype) are certainly more popular than their array siblings but do similar things.
const arr = ["a","b","c","d"];
const iterable = arr.entries();
iterable.next(); // {value: [0, "a"], done: false}
iterable.next().value; // [1, "b"]
iterable.next().value; // [2, "c"]
iterable.next(); // {value: [3, "d"], done: true}
After iteration completes, it cannot be restarted. The only way to do the same again is by creating a new iterable with the same method.
But what are the use-cases for such a method? Well, the .next()
method certainly gives you a bit more control ✊ over how your array is iterated. Also, key-value-like pairs returned by .entries()
can definitely prove to be useful, when wanting to have access to the element’s value and index at the same time. But, in these cases, maybe standard objects or maps (which I talked about in the previous post) will serve you better.
.keys()
With all complexity behind iterables already covered, there are two more methods similar to .entries()
- .keys()
and .values()
. The first one, as the name suggests, returns iterable with values equal to source array indices (aka keys). 🔢 Instead of key-value arrays, it returns numbers representing the indices of your array’s elements.
const arr = ["a","b","c","d"];
const iterable = arr.keys();
iterable.next(); // {value: 0, done: false}
// ...
.values()
The .values()
method, again, returns iterable. This time its values are equal to source array elements’ values.
const arr = ["a","b","c","d"];
const iterable = arr.values();
iterable.next(); // {value: "a", done: false}
// ...
.toString()
The last method I’d like to talk about is .toString()
. It’s present in JS objects, functions, strings, numbers, arrays and more! Possibly in every JS object (everything is an object)! But I think that still, with all its omnipresence, the .toString()
method is still not given as much interest as it deserves.
At its core, the .toString()
method simply converts an array to string. The returned value has a form of array elements placed tightly together, divided by commas.
const arr = [1,2,3,4];
const str = arr.toString(); // "1,2,3,4"
But its biggest advantage is that it doesn’t have to be called directly!
const arr = [1,2,3,4];
const str = `${arr}`; // "1,2,3,4"
In this way, every time your value needs to be converted to a string (e.g. string literals or concatenation), this method is called. This and also the fact that you can freely change this method with your own implementation gives you the ability to not only return custom strings but also perform certain operations when doing so! ✨ Trust me - this allows you to do some pretty interesting tricks!
Array time!
So, these were my personal picks of some of the most interesting and worth-looking array API methods! Did you know all of them? I know that built-in API can be easily forgotten and thus lead to us searching for solutions to problems that don’t really need to exist. I hope that this post helped you solve at least some of them. 😀
Now, what do you think of this article? Write your opinions in the comment section, and let me know if you liked it with a reaction below! Also, follow me on Twitter 🐦, my Facebook page and sign up for the newsletter to stay up-to-date ⏱ with the latest content from this blog. As always, thank you for reading this post and I’ll see you in the next one! ✌
If you need
Custom Web App
I can help you get your next project, from idea to reality.