So far, you have used variables to store singular values: a number, a boolean, a string, or even a function. But what if you want to store multiple values in a variable (e.g. 3 numbers, 5 strings, 2 numbers and 3 strings, etc.)? We can accomplish this with arrays.
An array is like a locker room. Each locker has a number, starting with 0. You
can put anything into a locker. The locker number is called an index, and each
locker is called an element (same name as a html element, but for different
purposes/usage). The value of the element is the contents of the locker.
Please note that arrays in JavaScript are zero-based which means that JavaScript starts counting from zero when it indexes an array. The first element in an array has an index value of zero.
The following examples will show you how to create an array and access its data.
const trees = ['pine', 4, 'apple', 6, false, () => {}] // Example of an array.let res = trees[0] // trees[0] refers to the element at index 0 in array trees,// and the value of that element (which is "pine") is assigned// to the variable res.trees[4] = trees[0] + trees[2] // what is trees?trees[5] = trees[1] + trees[3] // what is trees?res = trees.length // res is 6. (total 6 elements)// Like strings, arrays also have a special property called length.
You may notice that we are declaring the variable trees using const. This
is intentional and it is not an error. The reason will be explained later in
this chapter.
const trees = ['pine', 4, 'apple', 6, false, () => {}] // Example of an array.let res = trees[0] // trees[0] will get the element at index 0, which is "pine"trees[4] = trees[0] + trees[2] // ["pine", 4, "apple", 6, "pineapple", () => {}]trees[5] = trees[1] + trees[3] // ["pine", 4, "apple", 6, "pineapple", 10]
Question from students:
trees went from
["pine", 4, "apple", 6, false, ()=>{}] to
["pine", 4, "apple", 6, "pineapple", () => {}].
What happens to the false (element at index 4)?
= means left side takes the value of the right side. In other words,
assign the value of the right side to the left side.a = 10a = 20// a is 20 because it took the value 20// in tech terms: assign 20 to variable a.
trees[4] = 20 reads like this:
the element at index 4 in array trees (left side) takes the value of 20
(right side)
Arrays can store any data, including other arrays and functions. Try to work through the comment questions in the below examples. They may look strange, but if you leverage the knowledge you already have, you should be able to work out the answers.
const barks = [[1, 2, 3],[2, 3, 4],[3, 4, 5]]barks[2][1] = 10 // what is barks?barks[1][0] = barks[2][1] // what is barks?barks[2] = () => {barks[0][1] = barks[0][1] + barks[0][0]}// what is barks?barks[2]() // what is barks[2]()?barks[1] = barks[2]()// In the end, what is barks?// fyi, barks is a 2-dimensional array
const barks = [[1, 2, 3],[2, 3, 4],[3, 4, 5]]barks[2][1] = 10 // [[1,2,3], [2,3,4], [3,10,5]]/*The computer reads from left to right.fyi: barks is a 2-dimensional arraybarks[2] is the element at index 2 which has a value of array [3,4,5]barks[2] refers to the 1st dimension and the element at index 2 whichis [3,4,5]barks[2][1] -- [2] refers to the 1st dimension,whereas [1] refers to the 2nd dimension.barks[2][1] meanselement at index 2 of 1st dimension and index 1 of 2nd dimensionwhich has a value of 4const barks = [[1,2,3], [2,3,4], [3,***4***,5]]barks[0] barks[1] barks[2] barks[2][1] has value of ***4***So barks[2][1] = 10means element at index 1 in array [3,***4***,5] takes the value 10*/barks[1][0] = barks[2][1] // [[1,2,3], [10,3,4], [3,10,5]]// ^ ^// barks[1][0] barks[2][1]barks[2] = () => {barks[0][1] = barks[0][1] + barks[0][0]}// the element at barks[2] has been replaced by a function// barks now has [[1,3,3], [10,3,4], function]barks[2]() // barks[2]() is undefinedbarks[1] = barks[2]()// barks now has [[1,4,3], undefined, function]
Although strings are similar to arrays because they both have a length
property and you can use index to access data, it is important to note that
strings are not arrays.
A string stores a series of characters like: comment = "The dog barks", whereas an array can store multiple values under the same variable name like: fruits = ["apples", "oranges", "pears"]
They are 2 different data types. Because of the way the underlying data is stored, there are things you can do to an array that you can't do to a string, and vice-versa.
const a = ['Thor', 'Thor', 'Thor']
const b = [() => {}, () => {}, () => {}]
const c = ['Peter', () => {}, [19, 'Quill']]
solution that takes in an array as a parameter and
returns a function. When the returned function is called, the array input is
returned.const solution = input => {return () => {return input}}
In JS0 you learned that numbers, strings, and booleans are primitive data types. The value of a primitive data type variable can only contain a single thing (be it a string or a number or whatever).
// Primitive data types are known as being immutable data types// because there is no way to change a primitive value once it gets created.let string1 = 'This is a string.'string1[1] = 'X'console.log(string1) * //* string1 still has 'This is a string.'// value of string1 cannot be changedstring1 = 'xyz'console.log(string1) // string1 now has 'xyz'// value of string1 has been totally replaced by 'xyz'// here, a new copy of string1 is created with value 'xyz'// and variable string1 gets reassigned to a new reference
All other data types are non-primitive data type.
For example, array is a non-primitive data type.
What this means is that when you assign an array to a variable, the variable stores an address (also known as a reference) to the actual array data, not the array data itself.
This concept actually exists in real life for the same reasons.
In real life, we use addresses when dealing with houses because they are not easily portable. Similarly, addresses are needed for non-primitive data types because they tend to be large and therefore cannot move around easily. At work, it is very common for software engineers to work with arrays with thousands of elements.
Let's walk through a few examples.
const a = [1, 2, 3]const b = [false,'spiderman',(a, b) => {return a - b}]
Let's dissect (step by step) what the computer would do when it sees the code above:
= means that a is.... (need to figure out the right side)[1,2,3], so it goes into its memory and finds an empty
place to store the data.a contains the address or reference of the array.= means that b is.... (need to figure out the right side)[false,"spiderman",(a,b)=>{return a-b}], so it goes
into its memory and finds an empty place to store the data. Note that this
will be a different size space than the space used to store a.b contains the address or reference of the array.Note: In the above example, a and b have different addresses.
const a = [1, 2, 3]const b = [1, 2, 3]const c = a === b // what is c?
const a = [1, 2, 3]const b = [1, 2, 3]const c = a === b // false, because a and b have different addresses// although they have the same content
const a = [1, 2, 3] === [1, 2, 3] // what is a?
const a = [1, 2, 3] === [1, 2, 3] // still false!// the computer is creating 2 arrays and they have different addresses
const musicians = [1, 2, 3, 4] // Since arrays are non-primitive data,// musicians contains the address to the musicians array data.musicians[0] = 'Mozart' // did the variable musicians change?musicians[1] = 'Beethoven' // did the variable musicians change?musicians[2] = 'Liszt' // did the variable musicians change?musicians[3] = 'Chopin' // did the variable musicians change?
const musicians = [1, 2, 3, 4] // Since arrays are non-primitive data,// musicians is an address to the array data.musicians[0] = 'Mozart' // nomusicians[1] = 'Beethoven' // nomusicians[2] = 'Liszt' // nomusicians[3] = 'Chopin' // no// The data changed, but `musicians` itself contains an address.// The address stayed the same.
For all non-primitive data, you should always declare them with a const
because their address does not change.
When declaring non-primitive variables, only use let when you plan to reuse
the variable to store other addresses. For example:
let musicians = ['Mozart', 'Beethoven']musicians = ['Liszt', 'Chopin']// musicians variable now holds a completely new address for a completely different array.
In many cases, software engineers introduce bugs into their codebase because they did not pay attention to the nature of non-primitive data types. The following examples will help you understand why you should be careful when working with non-primitive data.
const avengers = ['Banner', 'Stark', 'Odinson', 'Rogers']const team = avengersavengers[1] = 'Fury'// what is avengers?// what is team?
const avengers = ['Banner', 'Stark', 'Odinson', 'Rogers']const team = avengersavengers[1] = 'Fury'// both avengers and team contain the same address or reference,// so they both have the same values// ["Banner", "Fury", "Odinson", "Rogers"]
const guardians = ['Rocket', 2, 'Drax']const milano = guardiansmilano[milano[1]] = 'Quill'// What is guardians?// What is milano?
const guardians = ['Rocket', 2, 'Drax']const milano = guardiansmilano[milano[1]] = 'Quill' // This is equivalent to: milano[2] = "Quill"// guardians and milano contain the same address,// so they both have the value// ["Rocket", 2, "Quill"]
const planets = ['Morag', 'Aakon', 'Xandar']const worlds = planetsworlds[2] = planetsworlds[2][1] = 'Ego' // What is worlds?// What is planets?
const planets = ['Morag', 'Aakon', 'Xandar']const worlds = planets // both worlds and planets have the same address// to the same arraryworlds[2] = planets // worlds[2] now stores the address to itself:// ["Morag", "Aakon", worlds]worlds[2][1] = 'Ego'// worlds[2] is the address to worlds itself,// which is the same address as planets// Therefore, it's no different from writing planets[1] = "Ego"// or worlds[1] = "Ego"// both planets and worlds have:// ["Morag", "Ego", worlds]
const planets = ['Morag', 'Aakon', 'Xandar']let worlds = planetsworlds = ['Earth', 'Contraxia', 'Berhart']// what is worlds?// what is planets?
const planets = ['Morag', 'Aakon', 'Xandar']let worlds = planets // worlds now has the same address as planetsworlds = ['Earth', 'Contraxia', 'Berhart'] // worlds is the address of a// new array// worlds: ["Earth", "Contraxia", "Berhart"]// planets: ["Morag", "Aakon", "Xandar"]
We've seen several examples of how making assignments when working with non-primitive data can affect multiple variables. Our last example will look at what happens when an array is passed into a function. Run through the function like a computer would and figure out what's happening to each variable.
const fruitPrices = [100, 150, 200]const addTax = (arr, i = 0) => {if (i === arr.length) {return arr}arr[i] = arr[i] * 1.1return addTax(arr, i + 1)}const cost = addTax(fruitPrices) // what is cost?// what is fruitPrices?const isSame = fruitPrices === cost // what is isSame?
const fruitPrices = [100, 150, 200] // This is the only array that gets// created in this code snippet!const addTax = (arr, i = 0) => {if (i === arr.length) {return arr}arr[i] = arr[i] * 1.1return addTax(arr, i + 1)}const cost = addTax(fruitPrices) // [110, 165, 220]// fruitPrices is also [110, 165, 220] because they are the same arrayconst isSame = fruitPrices === cost // true, because they are the same array
To keep your solutions organized, you can create a new file for this section
(such as non-primitive.js) and a new test file (such as
non-primitive.test.js). Refer back to Preflight if you need a refresher on the
steps.
Write a function named selectiveZero that takes in an array and a number.
This function replaces the value of any elements of an array when the
element's value matches the given number with a value of 0.
selectiveZero([5, 2, 2, 9], 2) // [5,0,0,9]
Tests
describe('selectiveZero function', () => {it('should change multiple instances of the input number to 0', () => {const arr = [5, 2, 2, 9]fn.selectiveZero(arr, 2)expect(arr).toEqual([5, 0, 0, 9])})it('should return an empty array', () => {const arr = []const result = fn.selectiveZero(arr, 3)expect(result).toEqual(arr)})it('should not modify an array with no matches', () => {const arr = [8, 9, 1, "I'm a string"]const original = [...arr]fn.selectiveZero(arr, 6)expect(arr).toEqual(original)})})
Shape
const selectiveZero = (arr, val) => {}
Explanation
arr and a number val.i ) to track index of an array
which starts at 0.i equals the length of array arr,arr.arr at index i equals val,arr at that index i to equal 0.Code
const selectiveZero = (arr, val, i = 0) => {if (i === arr.length) {return arr}if (arr[i] === val) {arr[i] = 0}return selectiveZero(arr, val, i + 1)}
Write a function named largest that returns the largest number in an array.
largest([5, 3, 9]) // 9largest([-20, -2, -5, -10]) // -2
Tests
describe('largest function', () => {it('should find the largest of 3 numbers', () => {const result = fn.largest([5, 3, 9])expect(result).toEqual(9)})it('should find the largest of 4 negative numbers', () => {const result = fn.largest([-20, -2, -5, -10])expect(result).toEqual(-2)})it('should return undefined on an empty array', () => {expect(fn.largest([])).toEqual(undefined)})})
Shape
const largest = arr => {}
Explanation
arr .i ) to track index of the array
which starts at 0.currentLargest ) to keep track of
which values are largest at a given index which start at first index of
arr.i equals the length of arr,currentLargest.i of array arr is greater
than currentLargest,currentLargest to the value of the element in array arr at
index i.Code
const largest = (arr, i = 0, currentLargest = arr[0]) => {if (i === arr.length) {return currentLargest}if (arr[i] > currentLargest) {currentLargest = arr[i]}return largest(arr, i + 1, currentLargest)}
Write a function named firstXToZero that sets the value of the first X
elements in an array to 0, where X is the input number.
firstXToZero([0, 5, 9, 6], 3) // [0,0,0,6]firstXToZero([1, 5, 4, 3, 3], 2) // [0,0,4,3,3]firstXToZero([1, 5, 9, 6], 1) // [0,5,9,6]
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 changing 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, firstX) => {}
Explanation
arr and a number firstX.i ) to track index of an array
which starts at 0.i equals the length of the array arr,arr.i is greater than or equal to number firstX,arr.i in array arr to 0.Code
const firstXToZero = (arr, firstX, i = 0) => {if (i === arr.length || i >= firstX) {return arr}arr[i] = 0return firstXToZero(arr, firstX, i + 1)}
Write a function named allPrime that determines whether the value of every
element in an array is a prime number.
allPrime([2, 7, 9, 10]) // false, because 10 (2*5) is not primeallPrime([19, 13, 17, 11]) // true
Tests
describe('allPrime function', () => {it('should return true for an array of all primes', () => {const result = fn.allPrime([2, 3, 17, 19])expect(result).toEqual(true)})it('should return false for an array with some primes', () => {const result = fn.allPrime([0, 7, 11, 12])expect(result).toEqual(false)})it('should return true for an empty array', () => {const result = fn.allPrime([])expect(result).toEqual(true)})})
Shape
const allPrime = arr => {}
Explanation
arr.i ) to track index of an array
which starts at 0.isPrime to help us determine which element's
value is prime in the given array arr.i equals the length of array arr,i in array arr is not prime,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 allPrime = (arr, i = 0) => {if (i === arr.length) {return true}if (!isPrime(arr[i])) return falsereturn allPrime(arr, i + 1)}
increasing that determines whether the elements of
an array are ordered such that they represent a strictly ascending sequence
of numbers. This means that the value of each element (other than the first)
is greater than the value of the previous element.This time you might not want to start your index counter at 0. Just remember if the array only has one element, your function should return true.
increasing([2, 7, 9, 10]) // trueincreasing([19, 13, 17, 11]) // false, because 19 -> 13 is decreasingincreasing([2, 7, 7, 10]) // false. 7 -> 7 is technically not increasing
If you are asked this question during an interview, make sure to check with the interviewer what should happen if the input is an empty array! In this case, we choose to return true for empty arrays for convenience.
Tests
describe('increasing function', () => {it('should return true for an increasing array', () => {const result = fn.increasing([2, 7, 9, 10])expect(result).toEqual(true)})it('should return false for an array that decreases', () => {const result = fn.increasing([19, 13, 17, 11])expect(result).toEqual(false)})it('should return false if elements are repeated', () => {const result = fn.increasing([2, 7, 7, 10])expect(result).toEqual(false)})it('should return true for an array of one number', () => {const result = fn.increasing([51])expect(result).toEqual(true)})})
Shape
const increasing = arr => {}
Explanation
arr.i ) to track index of an array
which starts at 0.i is greater than or equal to the length of the array
arr,i in array arr is less than or equal
to the value of the element at previous index i - 1 in array arrCode
const increasing = (arr, i = 1) => {if (i >= arr.length) {return true}if (arr[i] <= arr[i - 1]) return falsereturn increasing(arr, i + 1)}
Now that you know how to create arrays and change the data inside the array, this section will show you the functions you need to run in order to add and remove data from an array.
The Push() method takes one or more elements as arguments and adds them to the end of an array. Push() returns the new length of the array.
const apples = ['Fuji', 'Gala']const trees = apples.push('Pink Lady', 'Washington')// apples is now ["Fuji", "Gala", "Pink Lady", "Washington"] because push adds// to the array apples, trees now contains the new length of the array (4)const peaches = [4, 8, 12]const plums = peachespeaches.push(plums)// What is peaches?// What is plums?plums[0] = 47// what is peaches?// what is plums?peaches[3][1] = 39// what is peaches?// what is plums?let nectarines = [1, 2, 3]nectarines = nectarines.push(6)// what is nectarines?
const apples = ['Fuji', 'Gala']const trees = apples.push('Pink Lady', 'Washington')// apples is now ["Fuji", "Gala", "Pink Lady", "Washington"]// because push adds to the array// trees contains the new length of the array (4)const peaches = [4, 8, 12]const plums = peachespeaches.push(plums)// peaches and plums refer the same array (same address) and they are both have:// [4, 8, 12, array]// Note: [4, 8, 12, peaches] or [4,8,12,plums] are also both correct answers.// we used array here to represent all of the possible answers:// [4, 8, 12, peaches]// [4, 8, 12, plums]// [4, 8, 12, peaches[3]]// [4, 8, 12, plums[3]]// [4, 8, 12, plums[3][3]]// [4, 8, 12, peaches[3][3]]// [4, 8, 12, plums[3][3][3]]// [4, 8, 12, peaches[3][3][3]]// ...plums[0] = 47// peaches and plums are the same array (same address) and they are both:// [47, 8, 12, array]peaches[3][1] = 39// peaches and plums are the same array (same address)// peaches[3] also has the same address so peaches[3] is the same array as peaches// [47, 39, 12, array]let nectarines = [1, 2, 3]nectarines = nectarines.push(6)// push returns the length of the new array, so nectarines is 4
Pop() method removes the last element in an array and returns the removed element.
If the array is empty, undefined is returned.
const states = ['Delaware', 'Missouri', 'Hawaii']const lastState = states.pop()// lastState is "Hawaii" and states is now ["Delaware", "Missouri"]const nuts = ['almond', 'pistachio', 'hazelnut']const seeds = nutsconst nutella = nuts.pop()// What is nutella?// What is nuts?// What is seeds?
const states = ['Delaware', 'Missouri', 'Hawaii']const lastState = states.pop() // "Hawaii"// state is now ["Delaware", "Missouri"]const nuts = ['almond', 'pistachio', 'hazelnut']const seeds = nutsconst nutella = nuts.pop()// nuts and seeds are the same array and they are both ["almond", "pistachio"]// nutella is "hazelnut"
Shift() is like pop() but in the opposite direction. It removes the
first element in the array and returns the removed element. It returns
undefined if the array is empty.
const states = ['Delaware', 'Missouri', 'Hawaii']const firstState = states.shift()// firstState is "Delaware" and states is now ["Missouri", "Hawaii"]const nuts = ['almond', 'pistachio', 'hazelnut']const seeds = nutsconst drupe = nuts.shift()// What is nuts?// What is seeds?// What is drupe?
const states = ['Delaware', 'Missouri', 'Hawaii']const firstState = states.shift()// firstState is "Delaware" and states is now ["Missouri", "Hawaii"]const nuts = ['almond', 'pistachio', 'hazelnut']const seeds = nutsconst drupe = nuts.shift()// nuts and seeds are the same array and they are both ["pistachio", "hazelnut"]// drupe is "almond"
Unshift() is like push but in the opposite direction. It takes in one or more
elements as arguments and adds them to the beginning of the array, and
returns the new array length.
const nuts = ['almond', 'pistachio', 'hazelnut']const seeds = nutsnuts.unshift(seeds)// What is nuts?// What is seeds?const states = ['Pennsylvania', 'New Jersey', 'Georgia']const numStates = states.unshift('Delaware', 'New York')// what is states and numStates?
const nuts = ['almond', 'pistachio', 'hazelnut']const seeds = nutsnuts.unshift(seeds)// nuts and seeds are the same array (same address) and they are both:// [array, "almond", "pistachio", "hazelnut"]const states = ['Pennsylvania', 'New Jersey', 'Georgia']const numStates = states.unshift('Delaware', 'New York')// states is now ['Delaware', 'New York', 'Pennsylvania', 'New Jersey', 'Georgia']// unshift returns the length of new array, so numStates is now 5
The splice() method changes the contents of an array by removing or replacing existing elements and/or adding new elements in place.
The syntax of splice() is:
let arrDeletedItems = array.splice(start[, deleteCount[,
item1[, item2[, ...]]]])
start
The index at which to start changing the array.
If start is greater than the length of the array, start will be set to
the length of the array. In this case, no element will be deleted but the method
will behave as an adding function, adding as many element as item[n*] provided.
If negative, it will begin that many elements from the end of the array. (In
this case, the origin -1, meaning -*n* is the index of the nth last
element, and is therefore equivalent to the index of array.length - *n*.)
If array.length + *start* is less than 0, it will begin from index 0.
deleteCount | Optional
An integer indicating the number of elements in the array to remove
from start.
If deleteCount is omitted, or if its value is equal to or larger
than array.length - *start* (that is, if it is equal to or greater than the
number of elements left in the array, starting at start), then all the
elements from start to the end of the array will be deleted.
Note: In IE8, it won't delete all when deleteCount is omitted.
If deleteCount is 0 or negative, no elements are removed. In this case,
you should specify at least one new element (see below).
`item1, item2, ...` | Optional
The elements to be added to the array, beginning from index start. If you do
not specify any elements, splice() will only remove elements from the array.
Return value
An array containing the deleted elements.
If only one element is removed, an array of one element is returned.
If no elements are removed, an empty array is returned.
Let's see some examples:
const fruits = ['Banana', 'Orange', 'Apple', 'Mango']let removed = fruits.splice(2, 1, 'Lemon', 'Kiwi')// First, 1 element is removed starting at index 2, so fruits becomes// ["Banana", "Orange", "Mango"]// Next, the rest of the parameters are added in at index 2// Result: ["Banana", "Orange", "Lemon", "Kiwi", "Mango"]const fruits2 = ['Banana', 'Orange', 'Apple', 'Mango']removed = fruits2.splice(2, 2) // what is fruits2?removed = fruits2.splice(0, 1, 'Apple', 'Mango', 'Pear')// what is fruits2 and removed?removed = fruits2.splice(-2, 1) // what is fruits2 and removed?removed = fruits2.splice(1) // what is fruits2 and removed?
const fruits = ['Banana', 'Orange', 'Apple', 'Mango']let removed = fruits.splice(2, 1, 'Lemon', 'Kiwi')// First, 1 element is removed starting at index 2,// so fruits becomes ["Banana", "Orange", "Mango"]// Next, the rest of the parameters are added in at index 2// fruits has ["Banana", "Orange", **"Lemon", "Kiwi",** "Mango"]// removed has ["Apple"]const fruits2 = ['Banana', 'Orange', 'Apple', 'Mango']removed = fruits2.splice(2, 2)// remove 2 elements starting at index 2 ("Apple", "Mango")// fruits2 has ["Banana", "Orange"] (we didn't add anything)// removed has ["Apple", "Mango"]removed = fruits2.splice(0, 1, 'Apple', 'Mango', 'Pear')// fruits2 was ["**Banana**", "Orange"]// "Banana" at index 0 was removed,// and "Apple", "Mango", "Pear" added at its place (index 0)// fruits2 has ["Apple", "Mango", "Pear", "Orange"]// removed has ["Banana"]removed = fruits2.splice(-2, 1)// fruits2 was ["Apple", "Mango", "**Pear**", "Orange"]// remove 1 element, 2nd element from end which is "Pear"// fruits2 has ["Apple", "Mango", "Orange"]// removed has ["Pear"]removed = fruits2.splice(1)// fruits2 was ["Apple", "Mango", "Orange"]// remove all element from index 1 onwards ("Mango", "Orange" removed)// fruits2 has ["Apple"]// removed has ["Mango", "Orange"]
Write a function named copyArray that takes in an array and returns a new
array that is an identical copy of the given array.
copyArray(['Rocket', 'Groot', 'Star-Lord']) // ["Rocket", "Groot", "Star-Lord"]
Tests
describe('copyArray function', () => {it('should copy an array of 3 elements', () => {const result = fn.copyArray([-5, -23, 'study'])expect(result).toEqual([-5, -23, 'study'])})it('should not modify original array', () => {const original = ['hi',1,() => {return 5},'apple',45]const result = fn.copyArray(original)original[0] = 'Drax'expect(result[0]).toEqual('hi')})it('should copy an empty array', () => {const result = fn.copyArray([])expect(result).toEqual([])})})
Shape
const copyArray = a => {}
Explanation
You are given an array a.
We will need another variable (let's say it is result ) to store the copy
of the array and it starts at an empty array.
When the length of array result equals the length of array a,
result.Use the push() method with array result to push in the elements from
array a .
a to result.The push() method takes one or more elements as arguments and adds them
to the end of an array. Push() returns the new length of the array.
Continue.
Code
const copyArray = (a, result = []) => {if (result.length === a.length) {return result}result.push(a[result.length])return copyArray(a, result)}
Write a function named removeElement that takes in an array a and a
string as parameters. This function removes any element of the array a when
the element's value matches the value of the string. On completion, returns
the updated array a.
const a = ['Rocket', 'Groot', 'Groot', 'Star-Lord']const b = removeElement(a, 'Groot') // ["Rocket", "Star-Lord"]const c = b === a // since removeElement returns the original array,// b === a should be true
We haven't worked on removing array elements directly yet, but we did learn a function that can do the trick if you don't have any optional arguments...
Tests
describe('remove function', () => {it('should not remove anything', () => {const data = ['Rocket', 'Groot', 'Star-Lord']const result = fn.removeElement(data, 'Random')expect(result).toEqual(['Rocket', 'Groot', 'Star-Lord'])})it('should remove 1 element', () => {const data = ['Rocket', 'Groot', 'Star-Lord']const result = fn.removeElement(data, 'Star-Lord')expect(result).toEqual(['Rocket', 'Groot'])})it('should remove all elements', () => {const data = ['Rocket', 'Rocket', 'Rocket']const result = fn.removeElement(data, 'Rocket')expect(result).toEqual([])})})
Shape
const removeElement = (a, val) => {}
Explanation
You are given an array a and a string val.
We need another variable (let's call it i) to keep track of index in
array a. The index starts at 0.
When index i equals to the length of the array a,
a.When the value of the element at index i in array a equals to the value
of val, use splice to remove the element at index i in array a and
then call removeElement function for the next element.
The splice() method changes the contents of an array by removing or
replacing existing elements and/or adding new elements in place.
Continue.
Code
const removeElement = (a, val, i = 0) => {if (i === a.length) {return a}if (a[i] === val) {a.splice(i, 1)return removeElement(a, val, i) // Should not do i+1// because array just became smaller}return removeElement(a, val, i + 1)}
How is this different from the first exercise? Here we're not creating any new array, but rather modifying an existing one. So our recursive function only has to pass the address of the original array each time it's called.
In fact, because the function modifies its input, we could ignore the return
value or not return anything at all. Changing the base case from return a to
simply return and calling it with removeElement(a, "Groot") with no return
variable would still modify a.
Write a function named copyWithout that takes in an array and a number and
returns a copied array. This function copies all elements in the array except
for elements whose value matches the given number. On completion, returns the
copied array.
const result = copyWithout([5, 2, 2, 9], 2) // [5, 9]
Tests
describe('copyWithout function', () => {it('should copy without 2 middle elements', () => {const result = fn.copyWithout([5, 2, 2, 9], 2)expect(result).toEqual([5, 9])})it('should not modify original array', () => {const arr = [5, 2, 2, 9]fn.copyWithout(arr, 2)expect(arr).toEqual([5, 2, 2, 9])})it('should copy without last 2 elements', () => {const result = fn.copyWithout([2, 2, 2, 3, 3], 3)expect(result).toEqual([2, 2, 2])})it('should return identical array if no matches', () => {const result = fn.copyWithout([2, 6, 4], 3)expect(result).toEqual([2, 6, 4])})})
Shape
const copyWithout = (a, val) => {}
Explanation
a and a string val.i) to keep track of index in
array a . The index starts at 0.result) and it starts
with empty arrayi equals to the length of the array a,result .i in array a is not equal to string
val,i in array a into the array result .a .Code
const copyWithout = (a, val, i = 0, result = []) => {if (i === a.length) {return result}if (a[i] !== val) {result.push(a[i])}return copyWithout(a, val, i + 1, result)}
Write a function named copyReverse that makes a copy of an array in the
reverse order.
copyReverse([5, 2, 2, 9]) // [9, 2, 2, 5]
Tests
describe('copyReverse function', () => {it('should reverse copy an array of 4 elements', () => {const result = fn.copyReverse([1, 3, 5, 7])expect(result).toEqual([7, 5, 3, 1])})it('should not modify original array', () => {const arr = [1, 3, 5, 7]fn.copyReverse(arr)expect(arr).toEqual([1, 3, 5, 7])})it('should reverse copy an array of 1 element', () => {const result = fn.copyReverse(['bears'])expect(result).toEqual(['bears'])})})
Shape
const copyReverse = a => {}
Explanation
You are given an array a.
We need a variable (let's say it is result) to store a copy of array a
backwards and it starts as an empty array.
When the length of array result equals length of array a ,
result.Use unshift method to copy all the elements of array a to an empty
array result in reverse order. That is, start copying from the first
element of array a to the beginning of array result , then copy the 2nd
element of array a to the beginning of array result until all elements
of array a have been copied.
The unshift() method takes in one or more elements as arguments and
adds them to the beginning of the array, and returns the new array
length.
Continue
Code
const copyReverse = (a, result = []) => {if (result.length === a.length) {return result}result.unshift(a[result.length])return copyReverse(a, result)}
Write a function named copyLast that copies an array but leaves out a given
number of elements at the front.
copyLast(['Ironman', 'Thor', 'Captain', 'Black Widow', 'Hulk'], 2)// ["Captain", "Black Widow", "Hulk"]
Tests
describe('copyLast function', () => {const heroes = ['Ironman', 'Thor', 'Captain', 'Black Widow', 'Hulk']const original = [...heroes]it('should skip the first 2 elements', () => {const result = fn.copyLast(heroes, 2)expect(result).toEqual(['Captain', 'Black Widow', 'Hulk'])})it('should not modify original array', () => {fn.copyLast(heroes, 2)expect(heroes).toEqual(original)})it('should skip the first 0 elements (copy whole array)', () => {const result = fn.copyLast(heroes, 0)expect(result).toEqual(heroes)})it('should return empty array if skipping past array length', () => {const result = fn.copyLast(heroes, 6)expect(result).toEqual([])})})
Shape
const copyLast = (a, b) => {}
Explanation
You are given an array a and a number b as the inputs.
We need another variable (let's say result ) to store the new array which
starts with an empty array "".
We need another variable (let's say it is i) to keep track of index in
array a and it starts at 0
When index i is greater than or equal to the length of array a,
resultWhen index i is greater than or equal to b,
i in array a, and then push it into
result.The push() method takes one or more arguments and adds them to the end
of the array. Push() returns the new length of the array.
Continue
Code
const copyLast = (a, b, result = [], i = 0) => {if (i >= a.length) {return result}if (i >= b) {result.push(a[i])}return copyLast(a, b, result, i + 1)}
We've been asked to skip the first certain number of elements in the array, so
one easy way to do it is to start at the beginning and check each time if we've
reached the starting point yet. We could also have set i=b as the default
parameter.
The alternative solution without using the index variable i. The pushing
starts from the value of number b, which is the number of elements to skip.
Since index of array starts from 0, the index of the next element to push will
be the same as the value of number b. The value of variable b will be
incremented by 1 for the next element.
const copyLast = (a, b, result = []) => {if (b >= a.length) {return result}result.push(a[b])return copyLast(a, b + 1, result)}
The next exercise will challenge you to do the same type of modification, but on the other end of the array.
Write a function called copyFirst that copies an array but leaves out a
given number of elements at the end.
copyFirst(['Ironman', 'Thor', 'Captain', 'Black Widow', 'Hulk'], 2)// ["Ironman", "Thor", "Captain"]
Tests
describe('copyFirst function', () => {const heroes = ['Ironman', 'Thor', 'Captain', 'Black Widow', 'Hulk']const original = [...heroes]it('should skip the last 2 elements', () => {const result = fn.copyFirst(heroes, 2)expect(result).toEqual(['Ironman', 'Thor', 'Captain'])})it('should not modify original array', () => {fn.copyFirst(heroes, 2)expect(heroes).toEqual(original)})it('should skip the last 0 elements (copy whole array)', () => {const result = fn.copyFirst(heroes, 0)expect(result).toEqual(heroes)})it('should return empty array if skipping past array length', () => {const result = fn.copyFirst(heroes, 6)expect(result).toEqual([])})})
Shape
const copyFirst = (a, b) => {}
Explanation
a and a number b.i) to keep track of index in
array a and it starts at 0result ) which starts with an empty
array "".i is greater than or equal to a.length - b ,resulti in array a into result.The push() method takes one or more elements as arguments and adds them
to the end of an array. Push() returns the new length of the array.
Code
const copyFirst = (a, b, result = [], i = 0) => {if (i >= a.length - b) {return result}result.push(a[i])return copyFirst(a, b, result, i + 1)}
Write a function named runOnEach that calls a function on every element in
an array, and returns a new array with the results.
runOnEach([1, 2, 3, 4, 5], (e, i) => {// notice the input function has 2 parametersreturn e + i}) // returns [1, 3, 5, 7, 9]
Tests
describe('copyWithCall function', () => {const heroes = ['Ironman', 'Thor', 'Captain']const thanos = () => {return 'Thanos'}it('should call a function with two arguments', () => {const result = fn.runOnEach([1, 2, 3, 4, 5], (e, i) => {return e + i})expect(result).toEqual([1, 3, 5, 7, 9])})it('should not modify original array', () => {const original = [...heroes]fn.runOnEach(heroes, thanos)expect(heroes).toEqual(original)})it('should call a function with no arguments', () => {const result = fn.runOnEach(heroes, thanos)expect(result).toEqual(['Thanos', 'Thanos', 'Thanos'])})})
Shape
const runOnEach = (a, fn) => {}
Explanation
a and a function fn.i) to keep track of index in
array a and it starts at 0result ) to store the new array and
it starts with an empty array.i equals to the length of array a,result.fn with element at index i in array a as one
of the arguments.fn is index i.fn into array result.Code
const runOnEach = (a, fn, i = 0, result = []) => {if (i === a.length) {return result}result.push(fn(a[i], i))return runOnEach(a, fn, i + 1, result)}
How did you do on that one? The main difference between this and exercise 1 is
that here we're running the function on each element and its index. To work as
expected, your function had to pass both the element and the index to the fn
function—it's up to the called functions how many (if any) of their arguments
they use.
Write a function named onlyIndex that takes an array of arrays and returns
an array of each array's element at a given index. (If you think of the big
array as a table of rows, this function returns a given column).
onlyIndex([[1, 2, 3, 4, 5],[6, 9, 8, 7, 6],[4, 5, 6, 6, 7],[0, 1, 2, 3, 9]],2) // returns [ 3, 8, 6, 2 ]
Tests
describe('onlyIndex function', () => {const someNumbers = [[1, 2, 3, 4, 5],[6, 9, 8, 7, 6],[4, 5, 6, 6, 7],[0, 1, 2, 3, 4]]it('should not modify the original array', () => {const original = [...someNumbers]fn.onlyIndex(someNumbers, 2)expect(someNumbers).toEqual(original)})it('should pull out the element at index 2', () => {const result = fn.onlyIndex(someNumbers, 2)expect(result).toEqual([3, 8, 6, 2])})it('should pull out the element at index 0', () => {const result = fn.onlyIndex(someNumbers, 0)expect(result).toEqual([1, 6, 4, 0])})})
Shape
const onlyIndex = (a, b) => {}
Explanation
You are given an array of arrays a and a number b.
We need another variable (let's say it is i) to keep track of index in
array a and it starts at 0.
We need another variable (let's say result ) to store the new array and
it starts with an empty array "".
When index i is greater than or equal to the length of an array a,
resultWhen number b is greater than or equal to the length of first index i
of array a,
resultPush array a at index i and number b into array result.
Continue.
Code
const onlyIndex = (a, b, i = 0, result = []) => {if (i >= a.length) {return result}if (b >= a[0].length) {return result}result.push(a[i][b])return onlyIndex(a, b, i + 1, result)}
Complete the first six JS2 challenges