Working with arrays in JavaScript
Here are some helpful tips and tricks related to arrays in JavaScript
By: Ajdin Imsirovic 02 December 2019
This is an ultimate article on arrays in JavaScript. We’ll go in depth of this data structure in JS, and understand a number of different use cases, tips, tricks, and techniques.
Image by CodingExercises
Using the delete
method on an array member
If you use the delete
method on an array member, this doesn’t erase the value, it just replaces it with undefined
:
var arr = ['one','two','three','four'];
delete arr[0];
console.log(arr); // returns: [undefined, 'two', 'three', 'four']
Does a value exist in an array?
We check using the indexOf
method:
var arr = ['one','two','three','four'];
arr.indexOf('five'); // returns: -1
arr.indexOf('four'); // returns: 3
When it returns -1
, it means it can’t find the specified member in the array.
Alternatively, the includes
method returns true
if a matching member is found, and false
otherwise:
['one','two','three','four'].includes('one'); // returns: true
['one','two','three','four'].includes('five'); // returns: false
You can pass another argument to includes
to specify which member of array to start to search from:
['one','two','three','four'].includes('two', 1); // returns: true
['one','two','three','four'].includes('two', 2); // returns: false
Use Array.length to find the last member of array
Since Array.length counts an array from 1, and arrays are zero-indexed, it’s easy to get the last member of an array:
arr[arr.length - 1]; // returns: "four"
Use Array.length to quickly trim an array
You can change the length of an array on the fly, using Array.length property, like this:
arr.length = 2;
console.log(arr); // returns: [undefined, "two"];
Manipulating arrays with pop
The pop()
method is used to remove an item from an array. For example, on Google search results, we can get the all the h3 search results’ headings into an array like this:
var arr = [...document.getElementsByTagName('h3')];
arr.length;
arr.pop();
On the second row in the above code, we check the length of the array.
On the third row, we removed a member that was placed in the last position of arr
array.
On the fourth row in the above code, we check the length of the array again - now its’ length is smaller by one.
Manipulating arrays with push
The push
method is the opposite of pop
:
var arr = [];
arr.push('one');
arr.pop;
Manipulating arrays with shift
The shift
method is like pop
, only it removes items from the start, not the end:
var arr = ['one','two','three','four'];
arr.shift();
arr; // returns: ['two','three','four']
Manipulating arrays with unshift
The unshift
method is like push
, only it adds items from the start, not the end:
var arr = ['one','two','three','four'];
arr.shift();
arr.unshift('one');
arr; // returns: ['one','two','three','four']
Manipulating arrays with Array.slice and Array.splice
The slice
method returns a specified slice of the source array.
For example, we want to extract only the last two items from the below array:
var arr = ['one','two','three','four'];
arr.slice(2,3); // returns: ['three']
arr.slice(2); // returns: ['tree','four']
arr.slice(2,4); // returns: ['tree','four']
The Array.slice method is non-destructive.
The splice
method does two things:
- It removes specified items from an array
- It optionally puts new items in the removed slots
An example of removing the first entry in an array:
var arr = ['one', 'two', 'three', 'four'];
arr.splice(0,1); // returns: ['one']
arr; // returns: ['two','three','four']
The first argument to the splice
method specifies where to start splicing. The second argument specifies how many items to splice. The remaining arguments represent the optionaly new items to add in the place of removed slots.
Note that slice
is non-destructive, and splice
is destructive.
Destructuring Arrays
We can group multiple values in an array.
Thus, instead of writing:
var one = 'one';
var two = 'two';
var three = 'three';
var four = 'four';
…we can write:
var arr = ['one','two','three','four'];
But what if we want to go the other way round? To take members of array and make each of them individual variables?
That’s where array destructuring comes in:
var [one,two,three,four] = arr;
Now the variables are available individually:
console.log(one); // returns: "one"
You can also skip values, like this:
var [_1, , ,_4] = arr;
_1; // returns: "one"
_2; // returns: Uncaught ReferenceError: _2 is not defined at <anonymous>:1:1
_4; // returns: "four"
You can use the rest operator, ...
, to get back a sub-array:
var [_one, ...others] = arr;
console.log(_one); // returns: "one"
console.log(others); // returns: ['two','three','four']
Concat two arrays together
Let’s say we’ve got these two arrays:
var arr1 = ['one'];
var arr2 = ['two'];
We can combine them like this:
arr1.concat(arr2); // returns: ['one','two']
…but if we do this:
arr1; // returns: ['one']
Obviously, the concat
method doesn’t update arr1
. It’s an easy enough fix though:
arr1 = arr1.concat(arr2);
We can also concat two arrays using the spread operator ...
, like this:
var _arr1 = ['one','two'];
var _arr2 = ['three','four'];
var _arr1 = [ ..._arr1, ..._arr2 ];
_arr1; // returns: ['one','two','three','four']
Convert an array to a string
We can do this with the help of the join
method.
var arr = ['one','two','three','four'];
arr.join(); // returns: "one,two,three,four"
If you’re a musician, you might wanna return this:
arr.join(' and '); // returns: "one and two and three and four"
Flipping the order of items in an array
The destructive reverse
method makes this possible:
var arr = ['one','two','three','four'];
arr.reverse(); // returns: ['four','three','two','one']
Sorting arrays with Array.sort
It’s simple:
var arr = ['one','two','three','four'];
arr.sort(); // returns: ['four','one','three','two']
It’s also a destructive method.
There can be some unexpected behavior: capital letters are sorted before small letters:
var arr = ['one','One','two','Two'];
arr.sort(); // returns: ['One','Two','one','two']
Additionally, numbers are sorted based on the first digit in each item:
[300,20,1].sort(); // returns: [1,20,300]
[100,20,3].sort(); // returns: [100,20,3]
Thus, the sort
method, out of the box, is probably not the best tool to use.
However, it can still be used in certain situations, as long as we’re aware of its limitations. Sometimes, that simply means that we need to overcome the limitations of the sort function, by passing it a callback function to deal with the fact that it doesn’t sort numbers the way we’d expect.
How do we “fix” the sort function? We’ll start off with defining a new function declaration that we’ll call compareNums
:
function compareNums(nextNum, prevNum) {
return nextNum - prevNum;
}
The compareNums
method compares two numbers:
- if the
nextNum
number is larger than theprevNum
, it returns a positive value - if the
nextNum
number is the same as theprevNum
, it returns a0
- if the
nextNum
number is smaller than theprevNum
, it returns a negative value
Next, we’ll pass the compareNums
function as a callback to the sort
function, like this:
[100,20,3].sort(compareNums); // returns: [3,20,100]
To see the inner workings of our little sorting program, we can re-write it like this:
debugger;
function compareNums(next, previous) {
console.log(`${next} - ${previous}`);
return next - previous
}
[100,20,3].sort(compareNums);
Running the above code will return the same result as the previous one, except this time we’ll also see some output in the console:
20 - 100
3 - 20
Now we can think things through a bit better. The sort
method runs the compareNums
function. The first time it’s run, it looks like this:
compareNums(20, 100) {
console.log(20 - 100);
return 20 - 100
}
So, it returns -80
. The next time it’s run, it looks like this:
compareNums(3, 20) {
console.log(3 - 20);
return 3 - 20
}
So, it returns -17
.
The final output that’s returned from running all of the above code is:
[3,20,100]
It’s important to note that both times the compareNums
was ran, we got back negative numbers: the first time around, it was -80
, the next time around, it was -17
. The point is, both times it was ran, the returned result was a negative number.
Why does this work?
If we look at the definition of the sort function, we’ll find that it reads like this:
arr.sort([compareFunction])
The compare function is entirely optional. If we don’t provide it to the sort function, each member of the array will be coerced to string, then sorted. However, if we do provide the callback function, we need to give our callback function two params (in our example, the nextNum
and the prevNum
).
The way that the numbers are compared, works like this:
- If
nextNum
minuspreviousNum
returns less than zero, thenextNum
comes first - If the result returned is zero, both numbers are not moved in respect to one another, but they are moved in relation to all other elements.
- If
nextNum
minuspreviousNum
returns more than zero, thenextNum
comes second
Now we can improve our compareNums
function, like this:
function compareNums(next, previous) {
if (next < previous) {
console.log(`${next} is less than ${previous}, so ${next} comes first`);
return -1
}
if (next > previous) {
console.log(`${next} is greater than ${previous}, so ${next} comes second`);
return 1
}
console.log(`${next} is equal to ${previous} so we are not moving them`);
return 0;
}
[100,20,3].sort(compareNums);
One additional thing to note: The array is sorted in place - meaning, it uses no auxiliary data structure. Also, the sort
method doesn’t make a copy of the array it’s working on.
Next, we’ll see an example of solving a simple problem with arrays and some array methods, including sort
.
Exercise: Sort a todo list
Let’s say you have the following text in a txt file:
1. take out trash
3. learn js
2. go shopping
5. have some rest
4. watch a movie
How do you organize the above text using JavaScript, so that it’s ordered by numbers?
Here’s the solution.
First, we add the text:
var todos = `1. take out trash
3. learn js
2. go shopping
5. have some rest
4. watch a movie`;
Next, we replace the newline characters with a character that doesn’t exist in the string, then we split the string using the “weird” character.
todos = todos.replace( /\n/g, "|" ).split( "|" )
Next, we’ll use sort:
todos = todos.sort();
That’s it, we get the result we wanted:
["1. take out trash", "2. go shopping", "3. learn js", "4. watch a movie", "5. have some rest"]
Additional Exercise: Reverse the order of array
To make the order of todos descend rather than ascend, we can use the reverse
method:
todos = todos.reverse()
// returns: (5) ["5. have some rest", "4. watch a movie", "3. learn js", "2. go shopping", "1. take out trash"]
We can revert the array back to string:
todos = todos.join(",")
// "4. watch a movie,5. have some rest,2. go shopping,3. learn js,1. take out trash"
And now we can add a space after the ,
character:
todos.replace(",", ", ")
// returns: "5. have some rest, 4. watch a movie,3. learn js,2. go shopping,1. take out trash"
Looping over arrays
We can loop over arrays in a various number of ways.
I’ve collected 15 ways to do it, divided into two categories:
- “Traditional” methods of looping over arrays in JS
- Functional methods of looping over arrays in JS
Here’s the list of traditional methods we can use to iterate over arrays in JS:
for
loop- optimized
for
loop for-of
loopfor-in
loop - possible, but undesireable, because of quirks - basically, the issue is that prototype member variables are iterated over in thefor in
loop along with our array memberswhile
loopdo while
loop
Of these 6 looping methods above, the ones that are most commonly used are probably for
and for-of
.
There are also a number of functional methods for looping over arrays in JS. These methods exist on the Array.prototype
, and include:
Array.prototype.forEach
Array.prototype.filter
Array.prototype.filter
followed byArray.prototype.each
so as to avoid the problem of not being able tobreak
orcontinue
Array.prototype.map
Array.prototype.reduce
Array.prototype.some
Array.prototype.every
Array.prototype.includes
Array.prototype.find
Array.prototype.sort
Now that we have a list of all the different ways of looping over arrays, let’s see an example in which we’ll loop over the exact same array, using all of these methods.
The array we’ll be looping over loops like this:
let people = [
{ name: "John", age: 30, heightCm: 190, kilos: 110, salary: 5000, gender: "male" },
{ name: "Jane", age: 20, heightCm: 170, kilos: 65, salary: 8000, gender: "female" },
{ name: "Jack", age: 55, heightCm: 180, kilos: 75, salary: 4000, gender: "male" },
{ name: "Jill", age: 80, heightCm: 160, kilos: 50, salary: "", gender: "female" },
{ name: "James", age: 15, heightCm: 189, kilos: 77, salary: "", gender: "male" },
{ name: "Joanna", age: 15, heightCm: 168, kilos: 58, salary: "", gender: "female" },
{ name: "Bob", age: 50, heightCm: 170, kilos: 60, salary: 5000, gender: "male" },
]
Now that we have the array, let’s loop over it.
1. Using the for
loop
for(let i = 0; i < people.length; i++) {
console.log(`${people[i].name} is ${people[i].age} and weighs ${people[i].kilos} kilograms.`)
}
The for
loop is pretty straightforward, we loop over each member of the array and print out a message to the console based on the current “row” of data.
But, what about ordering our characters by age, for example? Here it is:
for(let i = 0; i < arr.length; i++) {
// ??? what to put here ???
}
It seems kind of difficult to wrap our heads around how to go on about this if we haven’t seen it done before. The trick is in using a nested for loop.
for(let i = 0; i < people.length; i++) {
for(let j = 0; j < people.length; j++) {
if(people[i].age > people[j].age) {
let originalValue = people[i]; // store the original position in array so we can swap it
people[i] = people[j]; // move up the original position in array
people[j] = originalValue; // set to original Value (complete the swap)
}
}
}
It would be interesting to also turn on the debugger so as to inspect what happens in the inner for
loop on each iteration:
for(let i = 0; i < people.length; i++) {
for(let j = 0; j < people.length; j++) {
if(people[i].age > people[j].age) {
let originalValue = people[i]; // store the original position in array so we can swap it
people[i] = people[j]; // move up the original position in array
people[j] = originalValue; // set to original Value (complete the swap)
}
}
}
Another quick tip: our loop is now ordering the array from oldest to youngest. If we wanted to order it from youngest to oldest, we’d change just one line:
if(people[i].age < people[j].age) {
We’ve simply changed the original greater than operator (i.e >
), to the less than operator (i.e <
).
2. Using the “optimized” for
loop
The trick with the “optimized” for loop is simply the fact that we’re “caching” the array length, thus making the execution of the for
loop a bit faster:
for(let i = 0, arrLength = people.length; i < arrLength; i++){
// etc.
}
Everything else is the same as in the original for
loop example.
3. Looping over a JS array with for-of
ES6 introduced iterables to the JavaScript language. Along came a new flavor of the for
loop, the for-of
.
Any iterable object and any generator can be looped over using for-of
as follows:
for (const person of people) {
console.log(`${person.name} is ${person.age} and weighs ${person.kilos} kilograms.`)
}
4. Don’t loop over a JS array with for-in
It is possible to loop over a JS array with for-in
. However, this should never be done.
There are two problems with looping over a JS array with for-in
:
- It enumerates through all the properties; including those associated to the Object (even properties added onto the Object by the user)
- The order of elements is not neccessarily followed
While the second problem is not a major turn-off, the first problem is really a deal-breaker. Here’s why:
Object.prototype.myAddition = "whatever";
for (const person in people) {
console.log( people[person] )
}
The output? It included “whatever”:
{ name: "John", age: 30 ...}
{ name: "Jane", age: 20 ...}
{ name: "Jack", age: 55 ...}
{ name: "Jill", age: 80 ...}
{ name: "James", age: 15 ...}
{ name: "Joanna", age: 15 ...}
{ name: "Bob", age: 50 ...}
whatever
The people
temporary variable of our for-in
loop is actually same as the usual i
“iterator variable” in a regular for
loop. Here’s the proof:
Object.prototype.myAddition = "whatever";
for (const person in people) {
console.log( person )
}
What do you think the above code will output? It will actually output numbers from 0 to 6, for all the members of the people
array. Additionally, it will also print out myAddition
, the name of the property we added to Object.prototype
.
5. Looping over a JS array with the while
loop
The while
loop is very similar to a for
loop. Here’s how it works with our example.
let count = 0;
while(person = people[count++]) {
console.log(`${person.name} is ${person.age} and weighs ${person.kilos} kilograms.`)
}
// alternative approach:
let count = 0;
while (people.length > count) {
console.log(`${people[count].name} is ${people[count].age} and weighs ${people[count].kilos} kilograms.`);
count++;
}
6. Looping over a JS array with the do while
loop
The do while
loop is almost identical to the while loop. The difference here is that it will always run at least once, contrary to the while
loop for which it is possible not to be run at all under some conditions.
let count = 0;
do {
console.log(`${person.name} is ${person.age} and weighs ${person.kilos} kilograms.`);
count++;
}
while(person = people[count++]);
// alternative approach
let count = 0;
do {
console.log(`${people[count].name} is ${people[count].age} and weighs ${people[count].kilos} kilograms.`);
count++;
}
while(people.length > count);
It’s common to use i
or key
instead of count
. Whatever works for you, that’s what you should use.
Functional approach to looping over arrays in JS
The functional approach to looping over arrays in JS involves different methods on the Array.prototype
: forEach
, each
, filter
, map
, reduce
, some
, sort
, and find
. We’ll discuss examples of each one of them in use, next.
7. Looping over arrays in JS with forEach
The forEach
method runs over each member of the array and runs a function we pass as an argument of the forEach
.
Simple example:
['one', 'two', 'three'].forEach( function(memberInArray) {
console.log(memberInArray);
});
The above code outputs:
one
two
three
Very often, the forEach
method’s argument is passed in as an ES6 function. Here’s the above example again, only this time with the arrow function instead of an ES5 function above:
['one', 'two', 'three'].forEach( memberInArray => console.log(memberInArray) );
The resulting output is the same.
Now we can try the forEach
on our people
array:
people.forEach( person => {
console.log(`${person.name} is ${person.age} and weighs ${person.kilos} kilograms.`)
})
The forEach
actually takes more than just one argument in the callback function (i.e the anonymous function that’s passed to it as an argument). Here’s the list of arguments that the forEach
callback can accept:
- current member of array that’s being iterated over (
person
in the above example ) - the index of the currently looped over member of array (usually an
i
or anindex
) - the array itself - i.e. the array that the
forEach
is used on (people
in the above example ) - the
thisArg
- the value ofthis
when executing the callback ( we’re not gonna discuss it here )
Ok, so out of the 4 possible arguments passed to the forEach
callback function, we’re going to discuss how to use 3.
So why would we need an index (the second variable)? Here’s one possible reason:
people.forEach( (person, index) => {
console.log(`${index}. ${person.name} is ${person.age} and weighs ${person.kilos} kilograms.`);
})
Doing this would log out the following in the console:
0. John is 30 and weighs 110 kilograms
1. Jane is 20 and weighs 65 kilograms.
2. Jack is 55 and weighs 75 kilograms.
3. Jill is 80 and weighs 50 kilograms.
4. James is 15 and weighs 77 kilograms.
5. Joanna is 15 and weighs 58 kilograms.
6. Bob is 50 and weighs 60 kilograms.
So this is an easy way to add a count on the output.
But why in the world would we need to give to the array that we’re looping over with forEach, the array itself?
Initially, it might seem strange, but actually there are completely valid scenarios for this. For example, if we use a named function as the callback of our forEach
:
let namedFn = function(item, index, arr) {
console.log(`${index}. ${item.name} is ${item.age} and weighs ${item.kilos} kilograms.`);
}
Now we can pass the named function as a callback to our forEach
:
people.forEach( namedFn );
A nice thing about this approach is that you can now use this namedFn
as the callback on any array. For example:
let people2 = [
{ name: "Michael Jordan", age: 55, kilos: 90 },
{ name: "Luka Doncic", age: 20, kilos: 85 }
]
people2.forEach( namedFn );
Now we’d get back:
0. Michael Jordan is 55 and weighs 90 kilograms.
1. Luka Doncic is 20 and weighs 85 kilograms.
And if we ran it on our initial people
array, we’d get back the output for the different array, using almost the same syntax.
8. Looping over arrays in JS with Array.prototype.filter
The filter
runs a test on a given array:
people.filter( eachPerson => eachPerson.age > 50 );
In the above example, we’re running the filter
method on eachPerson
in the people
array. The test we’re running on eachPerson
is whether their age
is over 50
.
This is the result running the filter on our people
array:
{name: "Jack", age: 55, heightCm: 180 ...}
{name: "Jill", age: 80, heightCm: 160 ...}
Now comes the fun part, combining the filter
and forEach
method to get the same result as using break
to stop iterating over the regular for
loop.
9. Using Array.prototype.filter
followed by Array.prototype.forEach
To understand this approach, we first need to look at when and how we need to use break
in a plain for
loop in JavaScript.
(function myFunction() {
var text = ""
var i;
for (i = Math.floor(Math.random() *100); i < 100; i++) {
if (i < 75) {
break;
}
text += "The number is " + i + "<br>";
}
document.body.innerHTML = "";
document.body.innerHTML = text;
})()
The above function is just a simple IIFE (immediately-invoked function expression) - a function that gets defined and called in one fell swoop.
The function sets the value of the i
variable in the for loop to a random integer, whose value is any number between 0 and 100.
The if condition checks if the random value of i
is less then 75, and if it is, we simply break out of the loop. This means that the line following the if statement will only be run if the random value of i
is set to any number between 75 and 100.
This simple example shows when we’d use break
. There’s another keyword: continue
. The continue
keyword can be thought of as a “soft break”. It doesn’t actually break out of the loop, but rather skips the current iteration of the loop and moves on to the next one:
(function myFunction() {
var text = ""
var i;
for (i = Math.floor(Math.random() *100); i < 100; i++) {
if (i < 75) {
continue;
}
text += "The number is " + i + "<br>";
}
document.body.innerHTML = "";
document.body.innerHTML = text;
})()
What the above code does is: for any number under 75, the current iteration will be skipped; in other words, if the random value of i
is any number under 75, all the numbers between 75 and 100 will always be printed. This is in contrast to the break
keyword, which would completely stop the for loop, i.e break out of it entirely.
Now that we understand the dynamics of break
and continue
, let’s run a regular, iterative for
loop on our people
array:
console.log("Here's the list of skinny people:")
for(let i = 0; i < people.length; i++) {
if ( people[i].kilos > 70 ) {
continue;
}
console.log(`
${people[i].name} is ${people[i].age} and weighs ${people[i].kilos} kgs.
`)
}
The output is as follows:
Jane is 20 and weighs 65 kilograms.
Jill is 80 and weighs 50 kilograms.
Joanna is 15 and weighs 58 kilograms.
Bob is 50 and weighs 60 kilograms.
If we used the break
keyword on the exact same code above, we’d get no results back. Why? Because we’d break out of the for
loop entirely, as soon as we’d run the if
statement on the first person, John
. Since we’re running break
for any person that’s over 70 kilos of weight, as soon as we hit that condition, we break out of the loop.
This leads us to the conclusion that in cases when we have an array like our people
array, it makes more sense to use the continue
keyword in an if
statement inside a for
loop.
Now that we’ve understood just what the break
and continue
keywords do in for
loops, we need to be able to know how to get the same results using the functional approach. It’s a two step process.
First, we filter
the array. This serves the same purpose that the continue
keyword does, skipping over array members that meet certain criteria.
Next, we run the “functional for loop” - the forEach
, on remaining array members, i.e on array members that pass the filter.
With filter
, we produce a whole new array, leaving the original array intact.
So:
people.filter(person => person.age > 50)
.forEach(person => `${person.name} is over 50 years of age.` )
Of course, we also need to bind a variable name to the new array this produces:
let over50 = people.filter(person => person.age > 50)
.forEach(person => console.log(`
${person.name} is over 50 years of age.
`)
)
10. Using Array.prototype.map
Similar to filter
, the map
method produces a new array from the existing array.
A trivial example of the map
would be running some kind of mathematical operation on an array of numbers. For our example, let’s say that we need to format all the objects in the people
array a bit differently. Would we ever need to do that in practice? Of course! Maybe we get back some weird JSON data from an API, and we need to arrange the objects so it can be consumed in our frontend.
Here’s how we could reformat array objects using the map
method:
let reformattedPeople = people.map(obj => {
let returnedObj = {};
returnedObj[obj.name] = obj.age;
return returnedObj;
})
The above code will use the existing 7 objects in the people
array, to produce the following 7 objects in the reformattedPeople
array:
[
{John: 30},
{Jane: 20},
{Jack: 55},
{Jill: 80},
{James: 15},
{Joanna: 15},
{Bob: 50}
]
11. Using Array.prototype.reduce
The reduce
method is one of those that needs to be practiced a few times before we really understand it. Once we do, however, it opens up lots of possibilities.
The first thing to know about reduce
is that it’s just another iterator method. An iterator is any array method that runs a used-provided function on each array member.
One way to explain reduce
is to compare it with map
.
Consider running the map
array iterator method on this array:
[1,2,3,4,5].map( item => item*2 );
The above code will return:
[2,4,6,8,10]
While the map
iterator returns an array, the reduce
iterator returns an accumulator.
Here’s another way of looking at reduce
: It’s just an iterator method which takes an operator and puts the operator between each member of the array. For example, running reduce
on [1,2,3]:
[1,2,3].reduce ( (accumulator, currentValue) => accumulator + currentValue );
// returns: 6
We can look at it like this too:
- We have an array of three numbers. Initially, the accumulator’s value is 0, because we haven’t given it an initial value (which is possible, and completely legitimate, as we’ll see later). Thus, on the first iteration, this is what the callback function’s values will be:
( 0, 1) => 0 + 1
. - On the second iteration, the value will be the total amount of the previous iterations, as the value of the
accumulator
, and thecurrentValue
will be2
, so:( 1, 2 ) => 1 + 2
. - On the third iteration, the value will be the total amount of all the previous iterations for the
accumulator
, plus thecurrentValue
for this iteration:( 3, 3 ) => 3 + 3
.
This ends all the iterations, and the return value is obviously 6
.
The reduce
iterator, of course, takes more than 2 arguments in the callback function. The additional arguments that it takes are the current iteration’s index in the array and the array itself.
Here’s the full “definition” for the reduce
iterator method:
Array.prototype.reduce(
(accumulator, currentValue, currentIndex, arrayItself ) => {
// whatever calculation you want to perfom on the array
}
)
There’s only one thing missing from the code above, and that’s the reduce
method’s callback function accumulator’s initial value. Here’s the reduce
method’s “signature” with accumulator’s initial value specified:
Array.prototype.reduce( callbackFn, initialAccValue);
Obviously, in the code above, the callbackFn
is actually this piece of code:
(accumulator, currentValue, currentIndex, arrayItself) => {
// whatever calc...
}
When we put all of this together, our reduce
iterator’s definition might also look like this:
Array.prototype.reduce(
(accumulator, currentValue, currentIndex, arrayItself ) => {
// whatever calculation you want to perfom on the array
}, initialAccValue);
)
Here are some use cases for the reduce
array iterator method:
- To sum up an array of amounts
- To sum up values from specific object keys in an array of objects
- To sum up values from an array of objects, excluding certain keys based on some criteria
- To find an average value of an array
- To count the number of times an item repeats in an array
- To flatten an array of arrays
- To build a pipeline of functions to execute (getting a really functional vibe here)
- To rearrange an array of objects into a different array of objects so that they are grouped by a specific property
- To extract and combine all the values from array values stores as keys in an array of objects
- To remove duplicate items in an array (an alternative approach to doing this using the Set data structure)
- To run promises in a sequence
- To re-write an implementation of the
map
array iterator method
As we can see, the reduce
method is very flexible and it is worth the investement of time to learn how to use it.
Without going into more complex scenarios listed above, let’s instead see a simple example of summing up an array of amounts.
Let’s say we want to calculate a shopping cart for drinks bought at a concert:
[ 9.99, 10.99, 11.99, 12.99 ].reduce( (acc, currentValue) => acc + currentValue)`
This example returns 45.96
. We can even use the accumulator’s initial value to count in, for example, the fixed price of a ticket. Let’s say the ticket’s price was 50
. Thus:
[ 9.99, 10.99, 11.99, 12.99 ].reduce( (acc, currentValue) => { return acc + currentValue }, 50)`
Easy stuff! Obviously, the returned value we’d receive from this operation would be 95.96
- the total cost of the concert ticket and drinks bought.
12. Using Array.prototype.some
The some
iterator method checks for at least one array element that passes a test, returning a Boolean true
or false
.
In other words, the some
method labels the following statement either true
or false
: There is at least one member of the array that passes the test.
For example:
// testing if value is greater than 100
let peopleTest = function(person, index, array) {
return person.kilos > 100
}
people.some( peopleTest )
The above code, when run, will return true
, because John
has 110
kilos of weight.
13. Array.prototype.every
While the some
method checks for presence of at least one array item that passes the test, the every
method checks if all items pass the test.
For example:
// testing if value is greater than 10
let peopleTest = function(person, index, array) {
return person.kilos > 10
}
people.every( peopleTest )
We’re checking if all the people weigh over 10
kilograms. Since the values in the people
array are realistic, the answer we get is obviously true
.
Let’s test another one:
// testing if value is less than 109
let peopleTest = function(person, index, array) {
return person.kilos < 110
}
people.every( peopleTest )
This time, we get back false, because John
in our people
array weighs exactly 110
kilograms.
14. Using Array.prototype.find
The find
method is a weird iterator. It will iterate all the members in the entire array, only if they all don’t pass the test.
You can imagine a valuable is gone missing and we’re trying to find the thief. The first person that has the valuable in their pocket is the thief, so there’s no point in iterating further.
Here’s an example:
// testing if value is less than 109
let peopleTest = function(person, index, array) {
return person.kilos < 110
}
people.find( peopleTest )
The returned value is:
{name: "Jane", age: 20, heightCm: 170, kilos: 65, salary: 8000, …}
Since John
weighs 110
kilograms, the find
method returned the very first person after him as the match, because she weighs only 65 kilograms, which is well under the 110 kilograms which was the passing test condition.
15. Using Array.prototype.sort
While the sort
method is also an array iterator, we’ve already covered it earlier in this article, under the title “Sorting arrays with Array.sort”.
Looping over arrays: Conclusion
With so many options described above, one of the things to look at before you loop over an array is: why are you looping?
Once you have an idea of what exactly it is that you’re trying to achieve, you can use one of many built-in array methods or looping constructs to do the task at hand.