Diving into Array.prototype.map, Array.prototype.find, and Array.prototype.some
Array.prototype.map
map
, find
, and some
are helpful JavaScript methods aimed at dealing with Arrays. To begin, let us first define some important terms.
Higher Order Function
: a function that accepts other functions as an argument, or, a function that returns other functions.
[1, 2, 3].map
(num => num * 2);
Callback function
: more simply, callback
, is a function that is passed to other functions.
[1, 2, 3].map(num => num * 2
);
Now let’s take a look at the map
method in action:
[1, 2, 3].map(num => num * 2);// Map Returns
// [2, 4, 6]
In the above, we are working with array [1, 2, 3]
, a map
method (Higher Order Function)
, and an arrow function
(Callback)
.
When we call map
on array [1, 2, 3]
, an iteration begins and each value of the current element is used as an argument to invoke the callback
. Bear in mind that this argument is represented in the arrow function by the parameter named num
.
When the code within the callback num * 2
is executed for each iteration, we get [2, 4, 6]
which map
returns as a new array.
Below, we can see evidence of map
returning a new array:
let originalArray = [1, 2, 3];
let mapValues = originalArray.map(num => num * 1);
originalArray[2] = 99;console.log(mapValues); //Logs [1, 2, 3]
console.log(originalArray); //Logs [1, 2, 99]
On line 1, we have a global variable declaration named originalArray
that is initialized with a reference to the[1, 2, 3]
array. On line 2, we declare another global variable named mapValues
, which captures the return value of calling the map method on originalArray
; the reference to [1, 2, 3]
array.
We then mutate originalArray
(line 3) by reassigning the value of its element at index 2 - by use of bracket notation - to the number literal 99.
As a result, when we call console. log using the reference originalArray
as an argument (line 5), what prints to the console is a mutated array [1, 2, 99]
.
When we call console.log using the reference mapValues
as an argument on the other hand - since map returns a new array - the console output is still a reference to the original [1, 2, 3]
array.
In a few short lines of code, we’ve proven that map
returns a new array. Now let’s move on to the next topic.
Return Value of map
Callback
Note the importance of the return value
of the callback
; the transformation criterion for map. Let’s look at a few examples.
Example 1: console.log
as the return value
let mapValues = [1, 2, 3].map(num => console.log(num * 1));
console.log(mapValues);// Console Prints **
// 1
// 2
// 3// Return Value** console.log(mapValues)
// [undefined, undefined, undefined]
The return value of the callback num =>console.log(num * 1)
, logs to the console 1, 2, 3,
and map
returns an array of undefined [undefined, undefined, undefined]
as a result of the callback that returns console.log on each iteration of [1, 2, 3]
; referenced by mapValues
.
(console.log implicitly returns undefined since it does not explicitly return anything)
Example 2: Missing return
statement inside Curly Braces {}
let mapValues = [1,2,3].map(num => {
num * 1;
});console.log(mapValues); // Logs [undefined, undefined, undefined]
In the above, notice how a missing return
statement - especially when combined with the use of curly braces ( line 1 to line 3) - returns undefined.
Because the return value of the map
callback will determine the new array it returns, a missing return statement will return undefined for each element of array[1, 2, 3];
making up the new array that map
returns.
[undefined, undefined, undefined]
Example 3: Boolean
as the return value
let originalArray = [1, 2, 3];
let mapValues = originalArray.map(num => (num * 1 !== 0) );console.log(mapValues); // Logs [true, true, true]
In this example, the return value of the callback of map
is boolean, so map
will return an new array of booleans
map
with Strings
There is no map method for strings, so we have to take another approach in the form of .split()
, and .join()
. Take for example, the code below.
let str = "I'll be back";
let stringToArray = str.split('');
str = stringToArray.map(char => char + char)
.join('');console.log(stringToArray);
// Logs [ 'I', "'", 'l', 'l',' ', 'b', 'e', ' ', 'b', 'a', 'c', 'k']console.log(str); // Logs II''llll bbee bbaacckk
On line 2, we use .split(‘’)
to turn the value of str
into an array of characters and reference that array to stringToArray
variable. In order to use map
, since it is an array method, this step is crucial.
On line 3, we then call map on stringToArray
where for each iteration, we use the arrays elements as an argument in the callback char => char + char
; the return value of which, is returned by map to a new array.
On line 4, we then use .join(‘’)
to combine the array returned by map resulting in II’’llll bbee bbaacckk
as the new the value in the reassignment of str
on line 3.
Notice that in both .split()
and .join()
we use an empty string ’’
as an argument, which means that we are splitting and joining, respectively, using each characters of the string (see line 6).
map
with Objects
Similar to working with strings, when using map
with Objects, we need to take another approach. In this section, we will be looking into Object.keys
, Object.values
, and Object.entries
; methods that convert objects to arrays, which make them useable, for instance, in our map method.
Lets first take a look at Object.keys
method:
let sport = {
basketball: ‘Kobe Bryant’,
boxing: ‘Manny Pacquiaw’,
soccer: ‘Christiano Ronaldo’,
golf: ‘Tiger Woods’
};let sportType = Object.keys(sport); //Line 8
let sportLogged = sportType.map(sports => console.log(sports));console.log(sportLogged);// prints
basketball
boxing
soccer
Golf//returns
//[undefined, undefined, undefined, undefined]
On line 8, we are calling Object.keys
passing through it as an argument, the object referenced by sport
, which returns an array made up of strings representing the keys (key-value pair) of the sport
object.
[‘basketball’, ‘boxing’, ‘soccer’, ‘Golf’]
.
These values, which will be used by map, is referenced by the variable named sportType
.
In the next line, we are calling map on the array referenced by sportType
, which if we remember clearly, will return a new array based on the return values of the callback. Since our callback return value is console.log
, we know that after printing its sports
argument (see line 9), map will return a new array of undefined
[undefined, undefined, undefined, undefined]
.
Lets see the code again using the Object.values()
method:
let sport = {
basketball: ‘Kobe Bryant’,
boxing: ‘Manny Pacquiaw’,
soccer: ‘Christiano Ronaldo’,
Golf: ‘Tiger Woods’
}let sportPlayers = Object.values(sport); //Line 8
let playersLogged = sportPlayers.map(players => console.log(players))console.log(playersLogged)// prints
Kobe Bryant
Manny Pacquiaw
Christiano Ronaldo
Tiger Woods//returns
//[undefined, undefined, undefined, undefined]
On line 8, we are using Object.values()
, passing through it as an argument, the object referenced by sport
, which returns an array [‘Kobe Bryant’, ‘Manny Pacquiaw’, ‘Christiano Ronaldo’, ‘Tiger Woods’]
; the values of sport
object. Note that this array is referenced by the sportPlayers
variable.
On line 9, we then call map
on sportPlayers
and on each iteration, our callback return of console.log
will print Kobe Bryant
, Manny Pacquiaw
, Christiano Ronaldo
, Tiger Woods
, and return [undefined, undefined, undefined, undefined]
; the values of which, are referenced by playersLogged
variable.
Lastly, let’s take a look at Object.entries()
method.
let sport = {
basketball: ‘Kobe Bryant’,
boxing: ‘Manny Pacquiaw’,
soccer: ‘Christiano Ronaldo’,
Golf: ‘Tiger Woods’
}let sportPlayersPair = Object.entries(sport); //Line 8
let sportPlayersPairLogged = sportPlayersPair.map(pair => console.log(pair));console.log(sportPlayersPairLogged);// prints
[ ‘basketball’, ‘Kobe Bryant’ ],
[ ‘boxing’, ‘Manny Pacquiaw’ ],
[ ‘soccer’, ‘Christiano Ronaldo’ ],
[ ‘Golf’, ‘Tiger Woods’ ]//returns
//[ undefined, undefined, undefined, undefined ]
When we call Object.entries()
and pass through it as an argument, the object referenced by sport
, we get a return of all the key-value pairs of object sport
in an array.
[
[ ‘basketball’, ‘Kobe Bryant’ ],
[ ‘boxing’, ‘Manny Pacqiaw’ ],
[ ‘soccer’, ‘Christiano Ronaldo’ ],
[ ‘Golf’, ‘Tiget Woods’ ]
]
Notice that this array is referenced by sportPlayersPair
variable (line 8).
Like we have seen before, the return of the callback (console.log
)through calling map on sportPlayersPair
will first print,
[ ‘basketball’, ‘Kobe Bryant’ ]
[ ‘boxing’, ‘Manny Pacquiaw’ ]
[ ‘soccer’, ‘Christiano Ronaldo’ ]
[ ‘Golf’, ‘Tiger Woods’ ]
and then return
[ undefined, undefined, undefined, undefined ]
which is the value that will be referenced by sportPlayersPairLogged
, as a result of map returning a new array.
Array.prototype.find
Understanding map
will serve us well in understanding find
, another JavaScript method.
Before diving deeper into that though, lets first remember the values that JavaScript evaluates to false:
‘’
, undefined
, null
, NaN
, 0
& false
This knowledge will prove useful in our understanding of find
. Now, let’s dive in!
[1, 2, 3, 4, 5].find(num => num > 2)// Find Returns
// 3
find
passes each element of the array its called on to its callback as an argument, returning only the first element value that evaluates to true
within that callback.
In our example, the return value of callback num => num > 2
is false when the value of num
is 1 and 2. However, when value of num
is 3
, the return value of the callback is true; which is why 3
is what find
returns.
Note that when the value of num
is 4 or 5, the callback also returns true but since we only care for the first instance of the callback returning true
, 4 and 5 will not be returned.
[1, 2, 3, 4, 5].find(num => num < 0)// Find Returns
// undefined
In the above, we can see that if a callback
doesn’t return a truthy value for any of the elements being used as an argument, find
will return undefined
.
To get a better understanding of find
, lets take a look at a few more examples:
Example 1: ‘hi’
let evaluatesToTrue = [1, 2, 3, 4, 5].find(num => ‘hi’);console.log(evaluatesToTrue) // Logs 1
When we call find
using [1, 2, 3, 4, 5]
array, its elements are being used as arguments in the num => ‘hi’
callback. Essentially, any value of num
in this callback will evaluate to true since 'hi'
is NOT one of the values that JavaScript implicitly treats as false
. With find
, we only care about the first value that evaluates to true, so on the first iteration of the array when the value of num
is 1, find returns it and ends execution.
Note that this value is captured in the variable evaluatesToTrue
.
Example 2: num + 1
let evaluatesToTrue = [1, 2, 3, 4, 5].find(num => num + 1);console.log(evaluatesToTrue) // Logs 1
Let’s take a step by step look at this example using what we know about find
. On the first iteration, find
passes the array element 1
, to the callback as an argument.
When num
represents 1, this is how our callback looks:
1 => 1 + 1
When 1 is passed as an argument, we get a return value of 2 - which evaluates to true - meaning, that this value of num
is the first and only value that find
will return. Notice that just like the previous example, any value of num
in this circumstance will evaluate to true, but with find
, we care only about the first .
Lets take another example: num — 1
let evaluatesToFalse = [1, 2, 3, 4, 5].find(num => num - 1);console.log(evaluatesToFalse);
Let’s take the same approach as before in understanding the code above.
On the first iteration, find
passes the array element 1
, to the callback as an argument.
When num
represents 1, this is how our callback looks:
1 => 1 - 1
When 1 is passed as an argument, we get a return value of 0 which evaluates to false. Remember the falsy values of JavaScript?
In this case, find
moves to the next iteration in the array and assigns element 2
, as the next argument to the callback.
2 => 2 - 1
When 2
is passed as an argument, we get a return value of -1 which - since its NOT one of the values that JavaScript treats as false - evaluates to true.
In this scenario, find
returns 2, which is initialized to the evaluatesToFalse
variable.
Example 3: console.log
let evaluatesToWhat = [1, 2, 3, 4, 5].find(num => console.log(num + 1));console.log(evaluatesToWhat);// evaluatesToWhat Prints ***
3
2
5
4
6// evaluatesToWhat Returns ***
undefined
In this example, let’s use what we’ve learned about using console log
as a return value in a callback. Remember, console.log
implicitly returns undefined
For each value of num
, console.log will return undefined
, print to the console the return value of num + 1
and move to the next iteration, eventually, returning undefined
. To reiterate, since there are no values of num
here that evaluates to true, find will return undefined
.
Example 4: Working with strings
let str = “I’ll be back”;
let stringToArray = str.split(‘’);
str = stringToArray.find(char => char === ‘k’)console.log(stringToArray);
// Logs [ ‘I’, “‘“, ‘l’, ‘l’,’ ‘, ‘b’, ‘e’, ‘ ‘, ‘b’, ‘a’, ‘c’, ‘k’]console.log(str); // k
Notice the use of .split()
(line 2) allowing us to take advantage of find
.
Example 5: Working with objects
let sport = {
basketball: 0,
boxing: ‘Manny Pacquiaw’,
soccer: ‘Christiano Ronaldo’,
Golf: 0
}let sportPlayersPair = Object.values(sport); //Line 8
let sportPlayersPairLogged = sportPlayersPair.find(values => values);console.log(sportPlayersPairLogged);
Manny Pacquiaw
On line 8, sportPlayersPair
is referencing the array
[ 0, 'Manny Pacquiaw', 'Christiano Ronaldo', 0 ]
In this example, remember that 0 is a falsy
value that find
will move forward from.
The next iteration on the other hand Manny Pacquiaw
, is NOT a falsy value and therefore WILL be returned by find
.
Array.prototype.some
Now that we have a firm grasp on map
and find
, let’s take a look at some
.
let trueOrFalse = [1, 2, 3].some(num => num > 1);console.log(trueOrFalse);
When we call some
on array [1, 2, 3]
, each element is passed once to its callback num => num > 3
as an argument, represented by the parameter num
.
In the first iteration, our callback look like this:
1 => 1 > 1
When 1 is passed as an argument, we get a return value of false
, so we move to the next iteration.
2 => 2 > 1
When 2 is passed as an argument though, we get a return value of true
.
Note that this is only the first return value we need to consider. The second return value to consider is the return value of the method call to some
.
In short, we are saying that some
uses the return value of its callback to determine its own return value.
In the example above, when the callback returns a truthy value, some
then returns true
.
Lets take a look at some more examples to help us understand some
:
Example 1: ‘hi’
let firstValue = [1, 2, 3].some(num => 'hi');console.log(firstValue);
true
Example 2: num + 1
let firstValue = [1, 2, 3].some(num => num + 1);console.log(firstValue);
true
Notice that in both examples, the callbacks num => ‘hi’
& num => num + 1
evaluate to true for any value of num
. We only care for the first value though, therefore, on the first iteration, some
will return true
and end execution.
Example 3: num — 1
let findTruthy = [1, 2, 3].some(num => num - 1);console.log(findTruthy);
true
In this example, when the value of num
is 1 on the first iteration, the callback returns a falsy value (Number Literal 0)and we move to the next iteration until we find a truthy value.
In the next iteration, when the value of num
is 2, we finally get a truthy value (Number Literal 1), where some
returns true
and ends execution. Note that this value is initialized to variable findTruthy
.
Example 4: [1, 1, 1]
let noTruthy = [1, 1, 1].some(num => num - 1);console.log(noTruthy);
false
In this case, some
will return false
because there are no array values in [1, 1, 1]
that when used as an argument to the callback num => num — 1
will return true
. So to the noTruthy
variable, we initialize with a value of false
.
Example 5: some
with strings
let str = “I’ll be back”;
let stringToArray = str.split(‘’);
str = stringToArray.some(char => char === ‘k’)console.log(stringToArray);
// Logs [ ‘I’, “‘“, ‘l’, ‘l’,’ ‘, ‘b’, ‘e’, ‘ ‘, ‘b’, ‘a’, ‘c’, ‘k’]console.log(str); // true
Notice the use of .split()
that will allow us to use some
.
Example 6: some
with Objects
let food = { a: 'corndog', b: 'hotdog', c: 'cat-dog' };
let charLength = Object.values(food).some(foodType => foodType.length < 3);console.log(charLength);
// falselet charLength2 = Object.values(food).some(foodType => foodType.length > 3);
console.log(charLength2);
// true
Notice the use of Object.values()
that will allow us to use some
.
In our example, the value charLength
is false while the value of charLength2
is true. The difference lies in the Relational Operators <
and >
. Take a closer look!
Conclusion
After all is said and done, we now have a solid understanding of map
, find
and some
. We know that all three methods - on each iteration - pass its array elements as arguments to its callback.
We know that map
returns a new array based on the return values of its callback. We know that find
returns the first element of its array ; where its callback returns a truthy
value. And finally, we also know that some
determines the boolean to return, based on the truthiness of its callback.
There was a ton to cover and it was exhilarating to cover.
Hopefully, those reading will have a better understanding of the topic and will be able to move forward more confidently like I will after spending a ton of time writing on it.
I hope my take was helpful, detailed and concise, and I look get better at writing such blogs, and get better at understanding JavaScript in general.
Thank you for reading!