Now that you know how to add and remove elements from arrays, we can now explore some functions that help you save time! We will be covering the most common functions used in JavaScript.
Let's say you have an array of people's ages:
const ages = [22, 28, 80, 48, 32]
Starting with this array, you want to get a new array of people's age 5 years
from now. You could write a function yourself, but this is such a common use
case that JavaScript arrays have a built in function that helps you do this
quickly, called map.
const ages = [22, 28, 80, 48, 32]const add5 = x => {return x + 5}const fiveYearsLater = ages.map(add5) // fiveYearsLater is [27, 33, 85, 53, 37]
Map takes in a function, applies the function to every element in the array, and returns a new array of the same length. The input function is called for every element and the return value from your input function is put into the new array. Note that the original array isn't changed.
When map runs the input function, it will actually pass in 3 arguments:
the current element in the array, the index of the current element, and the
original array itself. Notice that our add5 function above only used the first
parameter, and that's OK—functions don't have to have a parameter for every
argument that might be given. But let's see what each call to add5 actually
looked like:
const ages = [22, 28, 80, 48, 32]const add5 = x => {return x + 5}const fiveYearsLater = ages.map(add5)/*[ [22, → add5( 22, 0, [22, 28, 80, 48, 32] ) → 27,28, → add5( 28, 1, [22, 28, 80, 48, 32] ) → 33,80, → add5( 80, 2, [22, 28, 80, 48, 32] ) → 85,48, → add5( 48, 3, [22, 28, 80, 48, 32] ) → 53,32 → add5( 32, 4, [22, 28, 80, 48, 32] ) → 37] ]*/
Let's see two examples of how we might use this extra information that map
will pass in.
More Examples
// The input function is called with the element in the array// as well as the index of the element. It is rare to use the third parameter.const peppers = [5, 6, 6].map((element, index) => {return element + index}) // peppers will be [5,7,8] because [5+0,6+1,6+2]
Note that this is exactly what we asked you to implement in exercise 7 of the previous section. This makes it easier, doesn't it? See if you can figure out what's happening in the next example:
const melon = (delta, wax) => {return delta + wax + 1}const peppers = [5, 6, 6].map(melon).map(melon) // what is peppers?
const melon = (delta, wax) => {return delta + wax + 1}const peppers = [5, 6, 6].map(melon).map(melon)/*peppers is [7, 10, 12][5,6,6].map( melon ) returns [6, 8, 9]5 + 0 + 1 is 66 + 1 + 1 is 86 + 2 + 1 is 9[6,8,9].map( melon ) returns [7, 10, 12]6 + 0 + 1 is 78 + 1 + 1 is 109 + 2 + 1 is 12*/
Do all of the following using the map function. You can also solve these
problems without using map to compare and see the benefits that map brings!
Write a function called oddToZero that copies an array while turning all odd elements to 0.
oddToZero([1, 2, 3, 4, 5]) // returns [0,2,0,4,0]
Tests
describe('oddToZero function', () => {it('should zero when some elements are odd', () => {const result = fn.oddToZero([1, 2, 3, 4, 5])expect(result).toEqual([0, 2, 0, 4, 0])})it('should zero when all elements are odd', () => {const result = fn.oddToZero([1, 3])expect(result).toEqual([0, 0])})it('should return same array when no elements are odd', () => {const result = fn.oddToZero([8, 10, 12])expect(result).toEqual([8, 10, 12])})})
Shape
const oddToZero = arr => {}
Explanation
arr.arr.map() function to go through each element of the array arr.arr.map function:Code
const oddToZero = arr => {return arr.map(e => {if (e % 2) {return 0}return e})}
Although we're doing similar things to what we did in the last section, the
thinking is different here—instead of building an array piece-by-piece and
returning it, we're now returning individual elements that map will use to
build a new array.
Also note that unlike push, splice, and many others, arr.map doesn't
modify arr. Just because a function belongs to an object doesn't mean it
modifies that object.
Write a function called firstLetters that returns the first letter of each string in an array of strings.
firstLetters(['hello', 'my', 'name', 'is', 'pikachu'])// returns ["h", "m", "n", "i", "p"]
Tests
describe('firstLetters function', () => {it('should grab the first letters from 5 strings', () => {const result = fn.firstLetters(['hello', 'my', 'name', 'is', 'pikachu'])expect(result).toEqual(['h', 'm', 'n', 'i', 'p'])})it('should grab the first letters from 3 strings', () => {const result = fn.firstLetters(['JavaScript', 'is', 'awesome'])expect(result).toEqual(['J', 'i', 'a'])})it('should return an empty array when given an empty array', () => {const result = fn.firstLetters([])expect(result).toEqual([])})})
Shape
const firstLetters = arr => {}
Explanation
arr.arr.map() function to go through each element of the array arr.arr,Code
const firstLetters = arr => {return arr.map(e => {return e[0]})}
Change your firstXToZero function from the Non-Primitive section as follows:
map instead of recursionfirstXToZero([9, 1, 2, 2, 9], 3) // returns [0,0,0,2,9]firstXToZero([1, 2, 3, 4, 5], 2) // returns [0,0,3,4,5]firstXToZero([6, 7, 8], 3) // returns [0,0,0]
Tests
describe('firstXToZero function', () => {it('should change 3 numbers to 0', () => {const result = fn.firstXToZero([0, 5, 9, 6], 3)expect(result).toEqual([0, 0, 0, 6])})it('should not modify the array when asked to change 0 elements', () => {const result = fn.firstXToZero(["Don't", 'change', 'me'], 0)expect(result).toEqual(["Don't", 'change', 'me'])})it('should change all to zero when X beyond array length', () => {const result = fn.firstXToZero([1, 2, 3], 4)expect(result).toEqual([0, 0, 0])})})
Shape
const firstXToZero = (arr, x) => {}
Explanation
arr and a number x.arr.map() function to go through each element of arr.i is less than number x,Code
const firstXToZero = (arr, x) => {return arr.map((e, i) => {if (i < x) {return 0}return e})}
Write a function called nonPrimeToZero that copies an array while changing any number that isn't prime to 0.
nonPrimeToZero([1, 2, 3, 4, 5]) // [0,2,3,0,5]
Tests
describe('nonPrimeToZero function', () => {it('should zero all numbers when non-prime', () => {const result = fn.nonPrimeToZero([-13, 0, 1, 4, 6])expect(result).toEqual([0, 0, 0, 0, 0])})it('should return an identical array if all are prime', () => {const result = fn.nonPrimeToZero([2, 17, 1601, 7919])expect(result).toEqual([2, 17, 1601, 7919])})it('should change only prime numbers to 0', () => {const result = fn.nonPrimeToZero([1, 2, 3, 4, 5])expect(result).toEqual([0, 2, 3, 0, 5])})})
Shape
const nonPrimeToZero = arr => {}
Explanation
arr.isPrime to see if each element in the
arr is prime or not.nonPrimeZero,arr.map to run isPrime on each element,Code
const isPrime = (num, i = 2) => {if (num < i) return falseif (num === i) return trueif (num % i === 0) return falsereturn isPrime(num, i + 1)}const nonPrimeToZero = arr => {return arr.map(e => {if (!isPrime(e)) {return 0}return e})}
Write a function called append that copies an array of strings while adding a string to every string in it.
append(['hello', 'my', 'name', 'is', 'pikachu'], ' -log')// returns ["hello -log", "my -log", "name -log", "is -log", "pikachu -log"]
Tests
describe('append', () => {it('should append a string to 5 strings', () => {const result = fn.append(['hello', 'my', 'name', 'is', 'pikachu'],' -log')expect(result).toEqual(['hello -log','my -log','name -log','is -log','pikachu -log'])})it('should append a string to 2 strings', () => {const result = fn.append(['<img/>', '<p></p>'], '<hr/>')expect(result).toEqual(['<img/><hr/>', '<p></p><hr/>'])})it('should not modify the original array', () => {const arr = ['Spiderman', 'Peter Parker']fn.append(arr, 'Mary Jane')expect(arr).toEqual(['Spiderman', 'Peter Parker'])})})
Shape
const append = (arr, val) => {}
Explanation
arr and string val.arr.map() function to go through each element of the arrayarr.map(),val.Code
const append = (arr, val) => {return arr.map(e => {return e + val})}
Change your runOnEach function from the last section to use map. You can
use the same test cases.
runOnEach(['hello', 'my', 'name', 'is', 'pikachu'], e => {return e.toUpperCase()})// returns ["HELLO", "MY", "NAME", "IS", "PIKACHU"]runOnEach([1, 32, 904, 2955], e => {return e % 2})// returns [1, 0, 0, 1]
Shape
const runOnEach = (arr, val) => {}
Explanation
arr and a callback function val.val in arr.map().arr.map()const runOnEach = (arr, val) => {return arr.map(val)}
Note the following example will not work because it is returning the original array, not the new array.
const clone = arr => {return arr}
Tests
describe('clone function', () => {const farm1 = ['sheep', 'cow', 'pig']const farm2 = fn.clone(farm1)it('should clone an array of several elements', () => {expect(farm1).toEqual(farm2) // deep equality})it('should not return the same array', () => {expect(farm1 === farm2).toBeFalsy()})it('should clone an empty array', () => {expect(fn.clone([])).toEqual([])})})
Shape
const clone = arr => {}
Explanation
arr.arr.map() to go through each element in the array.Code
const clone = arr => {return arr.map(e => {return e})}
In the last exercise, you had to write a function that
returns a new array with the same elements as the input array, which means
cloning the input array. If you examine the solution, you'll see that we cloned
an array by using the map function:
const farm = ["sheep", "cow", "pig"]const farm2 = farm.map( (e) => {return e})} // farm2 is ["sheep", "cow", "pig"]
A quicker way of cloning an array is to use [...arr].
const farm = ['sheep', 'cow', 'pig']const farm2 = [...farm]
You can also combine arrays while cloning them:
const farm = ['sheep', 'cow', 'pig']const farm1 = ['horse', 'duck', 'llama']const farm2 = [...farm, ...farm, ...farm1]// farm2 is ["sheep", "cow", "pig", "sheep", "cow", "pig", "horse", "duck", "llama"]
forEach works exactly the same as map above. The only difference is that
forEach runs its input function on each element and returns undefined
rather than a new array. Just like with map, the input function will be given
the current element, its index, and the original array, and we can use this
information to write a function that modifies an array without returning
anything:
const ages = [22, 28, 80, 48, 32]const add5InPlace = (elem, idx, arr) => {arr[idx] = arr[idx] + 5 // We could also put arr[idx] = elem + 5// However, note that elem = elem + 5 will NOT work because elem is// a parameter of this function and won't modify the array!}ages.forEach(add5InPlace)// ages is now [27, 33, 85, 53, 37]
Note that we could have done this with map, but map by its name implies
"mapping" elements of one array to a new array, and so developers use forEach
in this case to make the intention of their code clear.
Answer the questions in the comments:
const arr = []const solution = data => {arr.push(data)}// At this point, what is arr?const arr2 = [1, 2, 3]arr2.forEach(() => {arr.push(arr2)})// At this point, what is arr?arr2[2] = 'three'// At this point, what is arr?
const arr = []const solution = data => {arr.push(data)}// []arr2 = [1, 2, 3]arr2.forEach(() => {arr.push(arr2)})/*[[1,2,3],[1,2,3],[1,2,3]]*/arr2[2] = 'three'/*[[1,2,"three"],[1,2,"three"],[1,2,"three"]]*/
Let's say you have an array of people's ages:
const ages = [22, 26, 80, 48, 32]
If you want to get a new array of all the ages under 27, you can use filter!
Filter takes a function that runs on each element in the original array to
decide whether to include it in the returned array. If the input function
returns a truthy value, the element will be in the array. If the input function
returns a falsey value, the element will be excluded from the array.
const ages = [22, 26, 80, 48, 32]const youngerThanCardiB = ages.filter(e => {return e < 27}) // youngerThanCardiB is [22, 26]
Unlike map, filter returns an array that could be smaller than the original
array.
Let's say you have an array of people's ages:
const ages = [22, 26, 80, 48, 32]
If you want to get an age that is greater than 77 from the array, use find!
const ages = [22, 26, 80, 48, 32]const olderThanHarrisonFord = ages.find(e => {return e > 77}) // olderThanHarrisonFord is 80
find takes in an input function, and returns the first element which the
function returns a truthy value for. If nothing is found, find returns
undefined.
Use filter to write a function called noMoreEvens that copies an array,
removing all even numbers.
noMoreEvens([1, 2, 6, 4, 5]) // [1,5]
Tests
describe('noMoreEvens function', () => {it('should remove evens from an array with a mix of numbers', () => {const result = fn.noMoreEvens([1, 2, 6, 4, 5])expect(result).toEqual([1, 5])})it('should remove all numbers when even', () => {const result = fn.noMoreEvens([2, 16, 40, 52])expect(result).toEqual([])})it('should not touch an array of all odd numbers', () => {const result = fn.noMoreEvens([1, 571, 3, 9])expect(result).toEqual([1, 571, 3, 9])})it('should remove negative even numbers as well', () => {const result = fn.noMoreEvens([-2, -571, -4])expect(result).toEqual([-571])})})
Shape
const noMoreEvens = () => {}
Explanation
arr.filter to remove the even numbers.Code
const noMoreEvens = arr => {return arr.filter(e => {return e % 2})}
Write a function that takes in an array of strings and removes the empty strings
removeEmpty(['hello', 'world', '', 'name', '', 'is'])// returns ["hello", "world", "name", "is"]
Tests
describe('remove empty function', () => {it('should return [] when array is empty', () => {const result = fn.removeEmpty([])expect(result).toEqual([])})it('should return [] when array only has empty strings', () => {const result = fn.removeEmpty(['', '', ''])expect(result).toEqual([])})it('should return same array when array has no empty strings', () => {const result = fn.removeEmpty(['hello', 'world'])expect(result).toEqual(['hello', 'world'])})it('should return array without empty strings', () => {const result = fn.removeEmpty(['hello', 'world', '', 'name', '', 'is'])expect(result).toEqual(['hello', 'world', 'name', 'is'])})})
Shape
const removeEmpty = arr => {}
Explain
Filter out empty array by using the filter function
Code
const removeEmpty = arr => {return arr.filter(str => {return str.length})}
Write a function called primesOnly that copies an array but keeps only prime numbers.
primesOnly([1, 2, 3, 4, 5]) // [2,3,5]
Tests
describe('primesOnly function', () => {it('should return empty array if no primes', () => {const result = fn.primesOnly([-13, 0, 1, 4, 6])expect(result).toEqual([])})it('should return an identical array if all are prime', () => {const result = fn.primesOnly([2, 17, 1601, 7919])expect(result).toEqual([2, 17, 1601, 7919])})})
Shape
const primesOnly = num => {}
Explanation
arr.isPrime to see if each value in the array is
prime or not.filter to filter prime numbers only.Code
const isPrime = (num, i = 2) => {if (num < i) return falseif (num === i) return trueif (num % i === 0) return falsereturn isPrime(num, i + 1)}const primesOnly = arr => {return arr.filter(e => {return isPrime(e)})}
You might have tried the following code and found that it didn't work:
const primesOnly = arr => {return arr.filter(isPrime)}
Why not? Although isPrime looks like it fits perfectly here, filter actually
passes not only the element but also its index and the array itself to the
function it calls (just like map and forEach). These extra arguments get
filled in as parameters in isPrime and break the recursion! So we have to use
a "wrapper" function to make sure isPrime gets the correct number of
arguments.
Write a function called firstPrime that finds the first prime number in an array.
firstPrime([1, 2, 3, 4, 5]) // 2
Tests
describe('firstPrime function', () => {it('should find a prime at the beginning of the array', () => {const result = fn.firstPrime([2, 17, 1601, 7919])expect(result).toEqual(2)})it('should find a prime at the end of the array', () => {const result = fn.firstPrime([1, 4, 16, 7919])expect(result).toEqual(7919)})it('should find no primes in an empty array', () => {expect(fn.firstPrime([])).toEqual(undefined)})})
Shape
const firstPrime = arr => {}
Explanation
arr.isPrime to see if the value of each index is
prime or not.arr.find() to located your first prime in the
array arr and return the first prime number you find in the array.Code
const isPrime = (num, i = 2) => {if (num < i) return falseif (num === i) return trueif (num % i === 0) return falsereturn isPrime(num, i + 1)}const firstPrime = arr => {return arr.find(e => {return isPrime(e)})}
Let's say you have an array of test scores for a team:
const scores = [22, 28, 80, 48, 32]
From the array, you want to get the sum of all the scores of that team (plus 19
points extra credit). You could write a function yourself, but this is such a
common use case that JavaScript arrays have a built-in function that helps you
do this quickly, called reduce.
const scores = [22, 28, 80, 48, 32]const addNumbers = scores.reduce((acc, e) => {console.log(acc, e)return acc + e}, 19) // addNumbers is 229/*Logs for acc, e every time the input function is run:19, 2241, 2869, 80149, 48197, 32*/
The reduce function has 2 parameters, a function and a starting value.
If you look at the documentation for reduce, you'll see that it says the second parameter is optional. This is considered bad practice in many companies, so you should always provide a starting value when calling reduce.
When the input function is called, it will receive 4 parameters:
the return value from the previous input function call or starting value (for the very first call),
the current element in the array,
the current index of the element,
and the array itself.
We don't always use all 4 parameters. Here we're using 3 of them:
let winter = [5, 6, 6].reduce((acc, element, index) => {return acc + element + index}, 100)// winter will be (100 + 5 + 0), then (105 + 6 + 1),// then (112 + 6 + 2) = 120
Although the accumulated result can be a number, it can also be another array:
const cherries = [1, 2, 3]const tomatoes = [5, 6, 7].reduce((farm, weight, tax) => {if (weight > 5) farm.push(weight + tax)return farm}, cherries) // what is tomatoes?
const cherries = [1, 2, 3]const tomatos = [5, 6, 7].reduce((farm, weight, tax) => {if (weight > 5) farm.push(weight + tax)return farm}, cherries) // tomatoes is [1,2,3,7,9]
reduce is a very important function that is used frequently, so make sure you
understand how it works before moving on.
Write a function called sum that adds up all the elements of an array.
sum([9, 8, 6, 2, 3]) // returns 28
Tests
describe('sum function', () => {it('should return 0 for an empty array', () => {const result = fn.sum([])expect(result).toEqual(0)})it('should return negative for array of negative numbers', () => {const result = fn.sum([-2, -3])expect(result).toEqual(-5)})it('should add up array with negative and postive numbers', () => {const result = fn.sum([-20, -3, 20])expect(result).toEqual(-3)})})
Shape
const a = (sum = arr => {})
Explanation
arr.arr.reduce() to add up all the elements in the
array.acc and element e as
arguments.arr.reduce() function, return the accumulator acc plus element e
.Code
const sum = arr => {return arr.reduce((acc, e) => {return acc + e}, 0)}
Rewrite your largest function from the Non-Primitive section to use
reduce. You can use the same tests.
largestReduce([9, 8, 16, 2, 3]) // returns 16
Tests
describe('largest function', () => {it('should find the largest number in the array', () => {const result = fn.largest([9, 8, 16, 2, 3])expect(result).toEqual(16)})it('should return undefined since given array is empty', () => {const result = fn.largest([])expect(result).toEqual(undefined)})it('should return first index of array if all numbers are same', () => {const result = fn.largest([10, 10, 10, 10])expect(result).toEqual(10)})})
Shapes
const largestReduce = arr => {}
Examples
arr.arr.reduce() to find the largest value.acc and element e as
arguments.arr.reduce(), if element e is greater than the accumulator acc,
return e.acc.arr[0] .Code
const largestReduce = arr => {return arr.reduce((acc, e) => {if (e > acc) {return e}return acc}, arr[0]) // start with the first element}
Write a function called longest that returns the longest string out of an array of strings.
longest(['Thor', 'Loki', 'Ant-Man', 'Rocket']) // returns "Ant-Man"
Tests
describe('longest function', () => {it('should find the longest string at the end of the array', () => {const result = fn.longest(['Thor', 'Loki', 'Rocket', 'Ant-Man'])expect(result).toEqual('Ant-Man')})it('should find the longest string in the middle of the array', () => {const result = fn.longest(['Thor', 'Spiderman', 'Ant-Man'])expect(result).toEqual('Spiderman')})it('should return string from array of length 1', () => {const result = fn.longest(['Wasp'])expect(result).toEqual('Wasp')})})
Shape
const longest = a => {}
Explanation
a.a.reduce() to find the longest string in the
array a.acc and element e as
arguments.a.reduce() function,e is greater than the length of accumulator,eacca[0].Code
const longest = a => {return a.reduce((acc, e) => {if (e.length > acc.length) {return e}return acc}, a[0]) // start with the first length}
Write a function called matches that counts how many times a given element occurs in an array.
matches(['Thor', 'Loki', 'Ant-Man', 'Loki'], 'Loki') // returns 2
Tests
describe('matches function', () => {it('should match elements in various positions', () => {const result = fn.matches(['Thor', 'Loki', 'Ant-Man', 'Loki'], 'Loki')expect(result).toEqual(2)})it('should match concurrent elements', () => {const result = fn.matches(['Spiderman', 'Spiderman', 'Mary Jane'],'Spiderman')expect(result).toEqual(2)})it('should return 0 if no matches', () => {const result = fn.matches(['Thor', 'Loki', 'Ant-Man'], 'Wonder Woman')expect(result).toEqual(0)})it('should return 0 if for an empty array', () => {const result = fn.matches([], 'Thor')expect(result).toEqual(0)})})
Shape
const matches = (a, val) => {}
Explanation
a and a value vala.reduce() to count how many times a given
element occurs in the array aacc and element e as
arguments.e equals value val , return accumulator acc + 1accCode
const matches = (a, val) => {return a.reduce((acc, e) => {if (e === val) {return acc + 1}return acc}, 0) // start with zero matches}
Write a function called combineLess5 that takes in an array of strings, and returns a combined string of all strings with length < 5.
combineLess5(['Thor', 'Loki', 'Ant-Man', 'Rocket', 'Wasp'])// returns "ThorLokiWasp"
Tests
describe('combineLess5 function', () => {it('should combine strings in various positions', () => {const arr = ['Thor', 'Loki', 'Ant-Man', 'Rocket', 'Wasp']const result = fn.combineLess5(arr)expect(result).toEqual('ThorLokiWasp')})it('should return one element with length < 5', () => {const arr = ['Spiderman', 'Loki', 'Ant-Man', 'Rocket']const result = fn.combineLess5(arr)expect(result).toEqual('Loki')})it('should return empty string if no matching elements', () => {const arr = ['Black Panther', 'Doctor Strange', 'Captain Marvel']const result = fn.combineLess5(arr)expect(result).toEqual('')})})
Shape
const combineLess5 = a => {}
Explanation
a.a.reduce()acc and element e as arguments.e is greater than or equal to 5,accacc + eCode
const combineLess5 = a => {return a.reduce((acc, e) => {if (e.length >= 5) {return acc}return acc + e}, '') // start with empty string}
Write a function called largerThan5 that takes in an array, and returns an array of numbers larger than 5.
filter function is better suited for this task, so try to do this with
filter as well.largerThan5([5, 9, 2, 6, 5]) // returns [9,6]
Tests
describe('largerThan5 function', () => {it('should find numbers larger than 5 from various positions', () => {const result = fn.largerThan5([5, 9, 2, 6, 5])expect(result).toEqual([9, 6])})it('should find concurrent numbers larger than 5', () => {const result = fn.largerThan5([8, 8, 2, 3, 10])expect(result).toEqual([8, 8, 10])})it('should return empty array if no numbers larger than 5', () => {const result = fn.largerThan5([1, 2, 3, 4, 5])expect(result).toEqual([])})})
Shape
const largerThan5 = a => {}
Explanation
a.a.reduce().acc and element e as arguments.e is greater than 5,e into accumulator acc.acc.Code
const largerThan5 = a => {return a.reduce((acc, e) => {if (e > 5) {acc.push(e)}return acc}, []) // start with an empty array}
In big data, the concept of map / reduce is used a lot.
Let's say you are Facebook and you want to count what is the most frequent first name.
Facebook has 2.5 billion users. If a computer could look up 100 users per second, then it would take about 10 months to go through all the users. Can you imagine waiting 10 months for your program to run? This is unacceptable.
The simple way to solve this problem is to split 2.5 billion into 10,000 groups of 250,000 users each.
Map: Facebook has 10,000 computers at its disposal, and each computer will
run through 250,000 users to figure out the most common first name and how many
times it shows up. You will get 10,000 first names and one result (name and
count) from each computer. An example result from a computer could look like
this: ["joe", 20000]. Assuming a computer can process 100 users per second, it
will only take 42 minutes to get these 10,000 names!
Reduce: Now that you have 10,000 first names, you can go through these 10,000 first names and count to find the most common first name. Assuming a computer process 100 users per second, this will only take 2 minutes!
Using map/reduce we have reduced our calculation time from 10 months to 44 minutes!
Now that you know a few array functions:
push, pop, shift, unshiftmap, filter, find, reduceWhat if you wanted to add new functions to arrays? For example:
[9,8,6,1].last() to return the last element of the array[1,2,3,4,5].evens() to return an array of even numbersHere are the steps to add your own customized function for arrays:
function( ... parameters ...) { ... code ...}.function and () => {} is described thoroughly in
the next lesson.Array.prototype.this keyword.this is a system keyword. Do not name your variables
this!When the function is running, this refers to the object that comes before ..
Array.prototype.last = function () {return this[this.length - 1]};[1, 2, 3].last() // When the last function is run, 'this' refers to [1,2,3]const jackfruit = [1, 2, 3].last() // What is jackfruit?
Array.prototype.last = function () {return this[this.length - 1]};[1, 2, 3].last() // When the last function is run, this refers to [1,2,3]jackfruit = [1, 2, 3].last() // 3
Array.prototype.papaya = function (i = 0, z = 0) {if (i === this.length) return zreturn this.papaya(i + 1, this[i] + z)}const juicy = [7, 8, 2].papaya // what is juicy?const juicy2 = [7, 8, 2].papaya() // what is juicy2?
Array.prototype.papaya = function (i = 0, z = 0) {if (i === this.length) return zreturn this.papaya(i + 1, this[i] + z)}const juicy = [7, 8, 2].papaya // functionconst juicy2 = [7, 8, 2].papaya() // 17
The examples above shows how you can add new functions into arrays. This approach is called prototype inheritance. But why?
Prototype - Because you are assigning your function to Array.prototype.
More details covered in the
next lesson.
Inheritance - Because in the following example, all existing arrays inherit the new function.
const a = [9, 8, 7]Array.prototype.greet = function () {return this[0]}a.greet() // returns 9/*Even though Array.prototype.greet is executed after a is defined,a still has the function greet.a inherited the function!*/
Create a prototype function called getEvens that returns a new array of
all the even numbers in an array of numbers.
const arr = [9, 80, 12, 2]arr.getEvens() // returns [80, 12, 2]
Tests
describe('getEvens function', () => {it('should pull even numbers from various positions', () => {const result = [9, 80, 11, 2].getEvens()expect(result).toEqual([80, 2])})it('should pull even numbers from concurrent positions', () => {const result = [2, 4, 6, 7, 8].getEvens()expect(result).toEqual([2, 4, 6, 8])})it('should have no result if no evens', () => {const result = [1, 3, 9, 21].getEvens()expect(result).toEqual([])})})
Shape
Array.prototype.getEvens = function () {}
Explanation
filter to filter out the even numbers in the
given array this.this.filter().Code
Array.prototype.getEvens = function () {return this.filter(e => {return e % 2 == 0})}
Create a prototype function called sum that adds all the elements of an
array together.
const arr = [9, 80, 12, 2]arr.sum() // returns 103
Tests
describe('sum function', () => {it('should find sum of an array of numbers', () => {const result = [2, 17, 3, -3].sum()expect(result).toEqual(19)})it('should add strings together', () => {const data = ['<p>', "<img src='https://placebear.com/800/710'>", '</p>']const result = data.sum()expect(result).toEqual("<p><img src='https://placebear.com/800/710'></p>")})it('should return undefined for an empty array', () => {const result = [].sum()expect(result).toEqual(undefined)})})
Shape
Array, (prototype.sum = function () {})
Explanation
this equals zero,reduce to calculate all the sum in the given
array this.'' or 0. To check what type of variable it is, we use
typeof function.this.reduce().Code
Array.prototype.sum = function () {if (this.length === 0) {return undefined}let startingValue = ''if (typeof this[0] === 'number') {startingValue = 0}return this.reduce((acc, e) => {return acc + e}, startingValue)}
Create a prototype function called pad that adds a given string to an array
a certain number of times.
const arr = ["<button name='submit'></button>", '<div></div>']arr.pad(2, '<br/>')// arr is ["<button name='submit'></button>", "<div></div>", "<br/>", "<br/>"]
Tests
describe('pad function', () => {it('should modify the original array', () => {const arr = ['Doctor']arr.pad(1, 'Strange')expect(arr).toEqual(['Doctor', 'Strange'])})it('should pad multiple times', () => {const arr = ["<button name='submit'></button>", '<div></div>']arr.pad(2, '<br/>')expect(arr).toEqual(["<button name='submit'></button>",'<div></div>','<br/>','<br/>'])})it('should return same array when given negative pad number', () => {const result = ['Quill', 'Gamora'].pad(-2, 'Drax')expect(result).toEqual(['Quill', 'Gamora'])})it('should return same array when given zero pad number', () => {const result = ['Quill', 'Gamora'].pad(0, 'Drax')expect(result).toEqual(['Quill', 'Gamora'])})})
Shape
Array.prototype.pad = function (num, str) {}
Explanation
num and a string str.num is less than or equal to 0,this.str into array this.Code
Array.prototype.pad = function (num, str) {if (num <= 0) {return this}this.push(str)return this.pad(num - 1, str)}
Create a prototype function called fizzbuzz that changes the original
array. All numbers divisible by 3 will be converted to "fizz", all numbers
divisible by 5 will be converted to "buzz", and all numbers divisible by both
3 and 5 will be converted to "fizzbuzz".
const arr = [9, 80, 12, 2, 30]arr.fizzbuzz()// arr is ["fizz", "buzz", "fizz", 2, "fizzbuzz"]
Tests
describe('fizzbuzz function', () => {it('should change numbers divisible by 3 to fizz', () => {const magicNumbers = [1, 2, 3, 6, 19, 18]magicNumbers.fizzbuzz()expect(magicNumbers).toEqual([1, 2, 'fizz', 'fizz', 19, 'fizz'])})it('should change numbers divisible by 5 to buzz', () => {const magicNumbers = [1, 2, 5, 10, 11]magicNumbers.fizzbuzz()expect(magicNumbers).toEqual([1, 2, 'buzz', 'buzz', 11])})it('should change numbers divisible by 15 to fizzbuzz', () => {const magicNumbers = [1, 2, 4, 15, 16, 30]magicNumbers.fizzbuzz()expect(magicNumbers).toEqual([1, 2, 4, 'fizzbuzz', 16, 'fizzbuzz'])})it('should correctly change 3 to fizz, 5 to buzz, and 15 to fizzbuzz', () => {const magicNumbers = [9, 80, 12, 2, 30]magicNumbers.fizzbuzz()expect(magicNumbers).toEqual(['fizz', 'buzz', 'fizz', 2, 'fizzbuzz'])})})
Shape
Array.prototype.fizzbuzz = function () {}
Explanation
this.this (let's say
i) which starts at 0.i equals the length of array this,this.this at index i is divisible by both numbers 5 and 3,this at index i equals "fizzbuzz".this at index i is divisible by 3,this at index i equals "fizz".this at index i is divisible by 5,this at index i equals "buzz".Code
Array.prototype.fizzbuzz = function (i = 0) {if (i === this.length) {return this}if (this[i] % 5 === 0 && this[i] % 3 === 0) {this[i] = 'fizzbuzz'}if (this[i] % 3 === 0) {this[i] = 'fizz'}if (this[i] % 5 === 0) {this[i] = 'buzz'}return this.fizzbuzz(i + 1)}/* (OPTIONAL) Below is a wrong implementation.* Let's say a coworker walks up to you and* tells you that there's a bug with his code...* It doesn't work with 15* Could you spot it?Array.prototype.fizzbuzz = function(i=0) {if (i===this.length) {return this}if (this[i] % 3 === 0) {this[i] = "fizz"}if (this[i] % 5 === 0) {this[i] = "buzz"}if (this[i] % 5 === 0 && this[i] % 3 === 0) {this[i] = "fizzbuzz"}return this.fizzbuzz(i + 1)}*//*AnswerBecause 15 is divisible by 3so this[i] is now "fizz""fizz" is not divisible by anything, so none of the following ifs match*/
Create a prototype function called removeEvens, which removes all the even
numbers from the array.
const arr = [9, 80, 12, 2]arr.removeEvens()// arr becomes [9]
Tests
describe('removeEvens function', () => {it('should remove even numbers from various positions', () => {const arr = [9, 80, 11, 2]arr.removeEvens()expect(arr).toEqual([9, 11])})it('should remove even numbers from concurrent positions', () => {const arr = [2, 4, 6, 7, 8]arr.removeEvens()expect(arr).toEqual([7])})it('should leave array the same if no evens', () => {const arr = [1, 3, 9, 21]arr.removeEvens()expect(arr).toEqual([1, 3, 9, 21])})it('should leave empty array the same', () => {const arr = []arr.removeEvens()expect(arr).toEqual([])})})
Shape
Array.prototype.removeEvens = function () {}
Explanation
this.this(let's say i) and
it starts at 0.i equals the length of array this ,this.this at index i is even,splice to remove that index value at array
this at index i.this.removeEvens() to update the given array with index i as
an argument.Code
Array.prototype.removeEvens = function (i = 0) {if (i === this.length) {return this}if (this[i] % 2 === 0) {this.splice(i, 1)return this.removeEvens(i)}return this.removeEvens(i + 1)}
getIterator that returns a function.
When the returned function is called, it returns the next element of the
array.Tests
describe('getIterator function', () => {it('should iterate through 3 elements', () => {const iterate = ['PayPal', 'Google', 'Netflix'].getIterator()expect(iterate()).toEqual('PayPal')expect(iterate()).toEqual('Google')expect(iterate()).toEqual('Netflix')})it('should return to beginning once done', () => {const iterate = [9, 80, 12, 2].getIterator()expect(iterate()).toEqual(9)expect(iterate()).toEqual(80)expect(iterate()).toEqual(12)expect(iterate()).toEqual(2)expect(iterate()).toEqual(9)expect(iterate()).toEqual(80)})it('should return undefined for empty array iterator', () => {const iterate = [].getIterator()expect(iterate()).toEqual(undefined)})it('should iterate through one element', () => {const iterate = ['Ironman'].getIterator()expect(iterate()).toEqual('Ironman')expect(iterate()).toEqual('Ironman')})})
Shape
Array.prototype.getIterator = function () {}
Explanation
i) that starts at -1.i is incremented by 1.thisCode
Array.prototype.getIterator = function () {let i = -1return () => {i = i + 1return this[i % this.length]}}
In the previous chapter you learned about the asynchronous nature of JavaScript. Here are two more examples to work through to tie this asynchronous functionality into what we've just learned about arrays and their helper functions.
const peppers = [5, 6, 6].map((element, index) => {setTimeout(() => {console.log(element)return 100}, 1000)}) // what is peppers?// Describe what is logged to the console.// Which values are logged and when are they logged?
const peppers = [5, 6, 6].map((element, index) => {setTimeout(() => {console.log(element)return 100}, 1000)}) // [undefined, undefined, undefined]// After 1 second, all 3 logs will be printed out at once// 5// 6// 6
Why is peppers [undefined, undefined, undefined] ?
Why do all 3 logs get printed out at once?
Because map runs the functions really fast and returns immediately. In a race, if all runners started immediately one after another, they would arrive at the destination at around the same time.
const friends = ['Tony Stark', 'Vision', 'Ultron'].reduce((a, b) => {setTimeout(() => {console.log('Greetings,', b)}, 2000)return a + b}, '') // What is friends?// What gets printed out into the console?
const friends = ['Tony Stark', 'Vision', 'Ultron'].reduce((a, b) => {setTimeout(() => {console.log('Greetings,', b)}, 2000)return a + b}, '') // "Tony StarkVisionUltron"// After 2 seconds, all 3 logs will be printed out at once:// Greetings, Tony Stark// Greetings, Vision// Greetings, Ultron
All your solutions must be solved recursively. You're not allowed to use
higher-order functions like forEach, reduce, filter, find, or map.
This is because you will be implementing these functions. Once you know how to
implement these functions, then you can freely use them in future lessons.
Complete the rest of JS2 challenges