Skip to content

Instantly share code, notes, and snippets.

@awhit012
Last active August 27, 2019 15:12
Show Gist options
  • Save awhit012/1122e1469503e3b4a8e8ddb75e7ef0cb to your computer and use it in GitHub Desktop.
Save awhit012/1122e1469503e3b4a8e8ddb75e7ef0cb to your computer and use it in GitHub Desktop.

Map

Objectives

  • Use .map() to mutate an array of objects
  • Articulate when it should be used over a 'for' or 'forEach' loop

Why?

In JavaScript we can always use for loops to iterate over arrays. However, arrays have many functions that can be performed on them that are more lexically clear. The .map function is designed for when we want to mutate items in an array and return a new, mutated array.

When?

.map is nondestructive and creates a new array.

We should only use .map if we are using the returned new array, and don't want to alter the original. Otherwise we can use forEach or a for loop.

Basic Example

We have an array of prices in dollars:

let prices = [1.99, 7.00, 4.85]

And we need to add a sales tax of 7% to each item:

We could use a for loop:

let totals = []
for(let i=0; i<prices.length; i++) {
	totals.push(prices[i] * 1.07)
}

or the more concise:

let totals = prices.map( item => item * 1.07)

We turned a 4 liner into a 1 liner! It's also quite easy to tell what is happening here. Each item is multiplied by 1.07 to add the tax.

Let's look at another example:

let caps = ["a", "b", "c"].map(char => char.toUpperCase())

Check for understanding

What would you expect newArray to equal in the following code?

let nums = [33, 25, 99, 12, 11, 54, 19, 80, 77]
let newArray = nums.map( num => num + 100)

Practice

Let's write together a map function that takes an array of booleans and returns a new array in which every item is the opposite of what it was.

For example:

[true, false, false, true] would become [false, true, true, false]

Multi-line and Conditional mapping

In the above examples, we have been using one liners. For those, we do not need explicate returns. However, if we wish to have a multiline callback, we simply use our return keyword to show what should end up in the new array:

["99", "00", "01", "02"].map(num => {
	let date
	if(parseInt(num) > 20) {
		date = "19" + num
	} else {
		date = "20" + num
	}
	return date
})
// ["1999", "2000", "2001", "2002"]

Practice

On your own, write code using .map that takes an array of integers, and returns the square root. If a number is negative, instead of returning NaN, return the square of the positive number, turn it into a string, and concatonate "i" to it.

For example [9, -16, 9] should be mapped to:

[3, '4i', 3]

We can assume that mixing data types in our array is not a problem

The Callback Arguments

In our above examples, we have been making use of the callbacks first arguement, which represents each item in the array.

The callback is provided with two additional arguments, the current index, and the array that is being mapped.

Note that we need parens around our arguments if we want to use more than one.

["a", "b", "c"].map( (num, index) => {
	let newNum = num + "--"
	return newNum + index
})
// ["a--0", "b--1", "c--2"]

You also have access to the array itself in the third argument, but this is not often used. Here's an example we use all three arguments.

let modifiedArray = [100, 200, 300, 400, 500].map( (num, index, array) => {
	if(index < array.length / 2) {
		return num + 1
	} else {
		return num
	}
})
// [101, 201, 301, 400, 500]

Using Named functions

In the example we've seen so far, we've used anonymous functions as our callback. We can also use named functions.

Let's refactor the above example to use a named function:

const addOneToHalfArray = (num, index, array) => {
	if(index < array.length / 2) {
		return num + 1
	} else {
		return num
	}
}

let modifiedArray1 = [100, 200, 300, 400, 500].map( addOneToHalfArray)

Note that you do not need to wrap this in an anonymous function and pass the three arguments to the callback. These get passed automatically.

Assessment:

  1. Refactor the following code to use the .map() function instead of a for loop.
  2. Next, refactor it to use a named function defined seperately
const posts = [
	{
	"id": 1,
	"title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
	"body": "quia et suscipit\nsuscipit recusandae"
	},
	{
	"id": 2,
	"title": "qui est esse",
	"body": "est rerum tempore vitae\nsequi sint nihil"
	},
	{
	"id": 3,
	"title": "ea molestias quasi exercitationem repellat qui ipsa sit aut",
	"body": "et iusto sed quo iure\nvoluptatem occaecati"
	},
	{
	"id": 4,
	"title": "eum et est occaecati",
	"body": "ullam et saepe reiciendis voluptatem adipisci"
	}
]

const htmlArray = []

for(let i=0; i<posts.length; i++) {
	let html = `<h1 id=${posts[i].id}>${posts[i].title}</h1><p>${posts[i].body}</p>`
	htmlArray.push(html)
}

Conclusion

  • Map is a function on the Array prototype
  • It should be used when you need to create a new array and leave the old one unchanged
  • It takes a callback with three arguments, item, index, array
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment