During interviews for software engineering positions, your ability to write functions will be tested. Your interviewer will not write down every detail for you describing what the function does. Instead, they will give you a few examples, briefly describe it, and see what you do.
Therefore, from this lesson on, all exercise problems will have short explanations to help train your ability to figure out what you are supposed to do from the examples. For each problem and during interviews, we highly recommend following these 5 steps:
If you don't completely understand the 5 steps above, don't worry. You will get an example for every exercise for the rest of the book
Note: It might be helpful to review Execution Context in JS0 before tackling this section.
Closure is a concept that allows a JavaScript function to keep track of the values it needs to run, allowing you to do incredible things. Closure simply means that:
An inner function always has access to the variables and parameters of its outer function, even after the outer function has returned.
Let's see an example. JS0 challenge 2 asked you to create a function that will add 3 numbers together. Now we'll add 3 numbers, but with a twist: We're going to create a function that, given 2 numbers, will return a new function that takes a third number and then adds all three together.
const solution = (a, b) => {return c => {return a + b + c}}const fun1 = solution(1, 2)// Even though solution has finished running, the a and b parameters still exist// a -> 1, b -> 2// Because of closure, fun1 can access variables a and blet res = fun1(3) // What is the value of res?res = fun1(5) // What is the value of res?res = solution(2, 3)(9) // what is the value of res?
const solution = (a, b) => {return c => {return a + b + c}}const fun1 = solution(1, 2)let res = fun1(3) // 1 + 2 + 3 = 6res = fun1(5) // 1 + 2 + 5 = 8res = solution(2, 3)(9) // 2 + 3 + 9 = 14
In the example above, notice how fun1
is aware of the arguments (a, b) that
were originally passed into its parent function (solution
).
Because of closure, a function's variables can change over time. Let's see how this plays out in the next example. Remember that every function has its own execution context (variables).
const solution = () => {let counter = 0return () => {counter = counter + 1if (counter < 3) {return 0}return counter}}const arya = solution()let res = arya() // what is res?const sansa = solution()res = arya() + sansa() // what is res?res = arya() + sansa() // what is res?res = arya() + sansa() // what is res?res = arya() + sansa() // what is res?res = arya() + sansa() // what is res?
const solution = () => {let counter = 0return () => {counter = counter + 1if (counter < 3) {return 0}return counter}}const arya = solution()let res = arya() // res is 0, but arya's counter has become 1const sansa = solution()res = arya() + sansa() // 0; arya's counter -> 2, sansa's counter -> 1res = arya() + sansa() // 3; arya's counter -> 3, sansa's counter -> 2res = arya() + sansa() // 7; arya's counter -> 4, sansa's counter -> 3res = arya() + sansa() // 9; arya's counter -> 5, sansa's counter -> 4res = arya() + sansa() // 11; arya's counter -> 6, sansa's counter -> 5
solution returns a function that has access to solution's counter
variable.
Each time the function runs, it updates the counter. Even though we got both
arya
and sansa
from solution, they're separate functions so they each have
their execution context, and their own copy of counter
.
Note that we followed good practice and used let counter = 0
. What would
happen if instead we just used counter = 0
?
const solution = () => {counter = 0 // because there is no let declaration,// counter is in the global scope.return () => {counter = counter + 1if (counter < 3) {return 0}return counter}}const arya = solution()let res = arya() // what is res?const sansa = solution()res = arya() + sansa() // what is res?res = arya() + sansa() // what is res?res = arya() + sansa() // what is res?res = arya() + sansa() // what is res?res = arya() + sansa() // what is res?
const solution = () => {counter = 0return () => {counter = counter + 1if (counter < 3) {return 0}return counter}}const arya = solution()let res = arya() // res is 0, counter -> 1const sansa = solution() // counter -> 0res = arya() + sansa() // 0; counter -> 2res = arya() + sansa() // 7; 3 + 4res = arya() + sansa() // 11; 5 + 6res = arya() + sansa() // 15; 7 + 8res = arya() + sansa() // 19; 9 + 10
What happened? When we created counter
with no let or const declaration, we
created it in the global scope. That means each function created by solution
doesn't get its own copy for its execution context; they all share the same
counter
. The reason this is bad practice is that functions become
unpredictable when they're allowed to change variables that other functions
might be expecting to stay the same.
From here to the rest of the lesson, the description of the function you are supposed to solve will be intentionally short and lacking to accurately reflect the life as a software engineer. No one will write out the instructions for you in detail. Instead, you will be given examples and you must figure out what the function does from the examples.
Write a function named hello3x
that returns a function. The returned
function should return "hello" only the first 3 times it's run, then "".
Example:
const threeF = hello3x()let b = threeF() // b is "hello"b = threeF() // b is "hello"b = threeF() // b is "hello"b = threeF() // b is ""// b will always be "" for all subsequent b = threeF() executions
Let's walk through how we will write this function using the five steps to solving interview problems: examples, function shape, think, code, test.
3 examples (Not many possibilities for examples here because the problem is straightforward)
// example 1:const b = hello3x()b() // returns 'hello'b() // returns 'hello'b() // returns 'hello'b() // returns ''// This is too simple, no need for other examples.
Function shape
hello3x
takes in 0 arguments and returns a function. The returned function
takes no arguments.
const hello3x = () => {return () => {}}
Explanation
hello3x
starts running.If you are confident that you've nailed down all the steps, start coding!
Code
const hello3x = () => {let counter = 0return () => {if (counter === 3) {return ''}counter = counter + 1return 'hello'}}
Test
Walk through your code like a computer, running it through your head with the examples in step 1 to make sure your code works
Write a function named helloFunction
that returns a function:
const moreHello = helloFunction()let b = moreHello() // b is "hello"b = moreHello() // b is "hellohello"b = moreHello() // b is "hellohellohello"b = moreHello() // b is "hellohellohellohello"// every time moreHello is called, one more "hello" will be appended to variable b.
Let's walk through how we wrote this function using the five steps to solving interview problems: examples, function shape, think, code, test.
Write 3 Examples:
// Example A:const b = helloFunction()let e = b() // e is "hello"e = b() // e is "hellohello"e = b() // e is "hellohellohello"e = b() // e is "hellohellohellohello"// every time the function is called, one more "hello" will be appended// to variable e.// Example B:const c = helloFunction()c // c is a function// This is too simple, no need for other examples.
Function shape: helloFunction
takes in 0 arguments and returns a
function. The returned function takes no arguments.
const helloFunction = () => {return () => {}}
Explanation
What variables do you need?
We need to declare the variable that allows us to change its value every time the function runs.
Where do you create the variable?
We can't put it in the returned function. If we do it there, then every time
the returned function is called, the variable is created and then removed
when function is done. So we must put it outside the returned function, right
when helloFunction
starts running.
When do you update the variable?
Every time the returned function runs, we update the variable by adding "hello" to the value of the variable. Return that variable.
Code
const helloFunction = () => {let result = ''return () => {result = result + 'hello'return result}}
Write a function named lessThan
that returns a function:
const youngerThanCardiB = lessThan(27)let miley = youngerThanCardiB(26) // true, because 26 is smaller than 27let nicki = youngerThanCardiB(36) // false, because 36 is not smaller than 27const smallerThan = lessThan(2)let b = smallerThan(3) // b is false, because 3 is not smaller than 2b = smallerThan(5) || youngerThanCardiB(5) // smallerThan(5) is false (falsey)// so b takes the value true because youngerThanCardiB(5) returns true
lessThan
is a function. You should know how
to write a function.Examples
//Example A:const youngerThanMary = lessThan(0)let b = youngerThanMary(0) // b is falseb = youngerThanMary(27) // b is falseb = youngerThanMary(22) // b is falseb = youngerThanMary(-1) // b is true//Example B:const youngerThanJohn = lessThan(-35)let c = youngerThanJohn(-35) // c is falsec = youngerThanJohn(0) // c is falsec = youngerThanJohn(42) // c is falsec = youngerThanJohn(-24) // c is falsec = youngerThanJohn(-39) // c is true//Example C:const youngerThanJane = lessThan(45)let d = youngerThanJane(45) // d is falsed = youngerThanJane(49) // d is falsed = youngerThanJane(39) // d is trued = youngerThanJane(24) // d is true
Function Shape
lessThan
takes in a parameter and returns a function. The returned function
takes in a parameter
const lessThan27 = n => {return n2 => {}}
Explanation
Before return - no need to do anything
Inside returned function - We need an if
statement to tell the function to
return a boolean that checks if n2
is smaller than n
Code
const lessThan = n => {return n2 => {return n2 < n}}
callWith
that takes a number and returns a function.
The returned function takes in a function as its parameter and invokes it
with the number as argument.If the above description sounds like a mouthful, don't worry. Nobody will (or
should) talk to you like that. Rather, you will be given examples (like below)
and you will need to figure out what the function callWith
does before
solving it.
const fun = callWith(10)let b = fun(num => {return num + 5}) // b is 15b = fun(num => {return num + 'hello'}) // b is "10hello"b = fun(num => {return 500 % num}) // b is 0
Wow, that was a lot of functions, wasn't it? Although it might have been intimidating that we kept saying "function," hopefully following the 5 steps helped you through the problem.
Examples
//Example A:const fun = callWith(10)let b = fun(() => {}) // b is undefinedb = fun(num => {return num + 'hello0'}) // b is "10hello10"b = fun(num => {return 50 + num}) // b is 60//Example B:const fun2 = callWith('hello')let c = fun2(num => {return num}) // c is "hello"c = fun2(num => {return num + ' 10hello'}) // c is "hello 10hello"c = fun2(num => {return 'world'}) // c is "world"//Example C:const fun3 = callWith(-10)let d = fun3(num => {return num + 38}) // d is 28d = fun3(num => {return num + '10'}) // d is "-1010"d = fun3(num => {return 10 * num}) // d is -100
Function Shape
callWIth
takes in an parameter and returns a function.
returned function takes in a parameter.
const callWith = num => {return f => {}}
Explanation
Before return - no need to do anything
Inside returned function - return the result of running the argument
Code
const callWith = num => {return f => {return f(num)}}
Write a function named runIt
that takes in a function as a parameter and
returns the function.
const subtract = runIt((a, b) => {return a - b})let b = subtract(3, 20) // b is -17b = subtract(11, 2) // b is 9
Examples
// Example A:const subtract = runIt((a, b) => {return a - b})let b = subtract(3, 20) // b is -17b = subtract(7, 4) // b is 3b = subtract(12, 6) // b is 6// Example B:const fun = runIt((a, b) => {return 'hello'})let c = fun(27, 4) // c is "hello"c = fun(12, 12) // c is "hello"// Example C:const fun2 = runIt((a, b) => {return a + b})let d = fun2(-10, 10) // d is 0d = fun2(2, 3) // d is 5d = fun2(9, 0) // b is 9
function shape
runIt
takes a parameter and returns a function
returned function takes in 2 arguments
const runIt = a => {return (b, c) => {}}
Explanation
Before return - no need to do anything
Inside returned function - return the result of running the a
Code
const runIt = a => {return (b, c) => {return a(b, c)}}
You may have noticed that some of the exercises and challenges in JS0 are actually closure examples.
If this section goes way over your head, skip it and come back to it later. How much later? Just keep coming back over and over again until you get it.
Sometimes when you run a function you want to write an HTML console.log()
method to display or print out what is going on in the console. Instead of
writing console.log()
into every function, you can create a function called
addLog
. When you want to write a function, you pass your function into
addLog
like this:
// Let's say you want to write a function that adds 2 numbersconst add2 = addLog('sum of 2 numbers', (a, b) => {return a + b})// Let's say you want to write a function that checks if the first number// is bigger than the second numberconst isFirstBigger = addLog('comparing 2 numbers', (a, b) => {return a > b})// Let's use these functions!let result = add2(5, 2) // result is 7if (isFirstBigger(result, 5)) {result = 100}// result is 100result = add2(result, 5)// result is 105/*At the same time, because add2 and isFirstBigger is created by addLogyou will see logs printed out!sum of 2 numbers, 5, 2comparing 2 numbers, 7, 5sum of 2 numbers, 100, 5These logs are incredibly helpful if you need to figure out what went wrong.*/
AddLog Implementation
const addLog = (msg, fn) => {return (a, b) => {console.log(msg, a, b)return fn(a, b)}}
Sometimes, when another developer calls your function, they may have nothing to pass to any of the parameters. To get around that, you can define default values for your parameters. In the next section, you'll see how this allows us to create all kinds of powerful functions. For now, see this example of a function that adds 1 to the sum of its parameters.
const add1 = (x = 3, y = 2) => {return x + y + 1}let res = add1 // what is res?res = add1(1) + add1() + add1(4, 5) // what is res?res = add1(5) === add1(2, 3) + 2 // what is res?
const add1 = (x = 3, y = 2) => {return x + y + 1}let res = add1 // functionres = add1(1) + add1() + add1(4, 5) // 20; [1+2+1] + [3+2+1] + [4+5+1]res = add1(5) === add1(2, 3) + 2 // true; 8 === 8
When a function has default parameters and you call the function with only some
arguments, the parameters will be filled with arguments starting from the left.
When we ran add1(1)
, x took the value 1 and y took its default value 2. When
we gave no arguments (add1()
), both x and y took their default values.
Finally, when we ran add1(4,5)
both x and y had values specified so neither
one took its default.
In the previous section, we created functions that return other functions that must be called again and again to get different results. What if instead we created a function that only needs to call itself? This is called recursion.
For example, say you wanted to add up all the numbers from 1 to 999. You would need to create a variable to hold the sum, then add 1 to the sum, add 2 to the sum, add 3 to the sum, ... add 999 to the sum, and then return the sum. Let's start with a much smaller number (3) so we can follow it through the whole process:
const addTo3 = (counter = 1, result = 0) => {if (counter > 3) {return result}return addTo3(counter + 1, result + counter)}let res = addTo3 // what is res?res = addTo3(2) // what is res?res = addTo3() // what is res?
const addTo3 = (counter = 1, result = 0) => {if (counter > 3) {return result}return addTo3(counter + 1, result + counter)}let res = addTo3 // functionres = addTo3(2) // 5/** return fun1(3, 2)* return fun1(4, 5)* return 5*/res = addTo3() // 6
The addTo3
function above adds up all the numbers from 1 to 3 (if no arguments
are given). When writing a function like this, it is important to think through
these steps before coding:
What variables do I need?
counter
.result
.We can just add these variables to the function parameters (if needed):
counter=1, result=0
.
When does the function stop?
When counter reaches 4, we would have finished adding all the numbers from 1-3,
so we can return result
to stop the function from executing anymore:
if (counter > 3) return result
.
If we are not at the end, how do we proceed?
Since we are not at the end, we just call the function again and increment
counter by 1, then add counter to our result:
return addTo3(counter + 1, result + counter)
.
Let's recap the steps for writing a recursive function:
Write a function named sumToMe that takes in a number and returns the sum of all numbers from 0 to that number.
const res = sumToMe(5) // res should be 15 because 5+4+3+2+1+0
This will be similar to our addTo3 example above, but now instead of counting up to a fixed number, we will count down from whatever number is passed in. (You can also write this function by using a counter. We opted not to do it here so we create fewer parameters.)
Parameters: In addition to the required parameter, we need a variable to
keep track of the sum. Let's call this variable sum
. sum should start with
a default value of 0.
Base case: When we've counted down to 0, there are no numbers left to add
so we should stop and return the result that we have collected:
if (input <= 0) return sum
.
We write input <= 0
in our check just in case someone used our function
incorrectly by passing in a negative number. For instance: sumToMe(-3)
.
Recursive case: The rest of the logic is simple; at each step we just add the current number to the result and decrease the number.
const sumToMe = (input, sum = 0) => {if (input <= 0) {return sum}return sumToMe(input - 1, sum + input)}
Some of these questions have been asked during onsite interviews, so make sure you attempt these problems and understand how to do them.
Make sure you think through the steps first before attempting to solve the problem.
Write a function named love that calls
console.log("The things I do for love")
99 times.
love()/*The things I do for loveThe things I do for love... (96 times)The things I do for love*/
Explanation: There are no parameters and no return values, so we don't need to provide any examples
Function Shape: love
does not take in any parameters and does not
return anything
const love = () => {}
Explanation:
Code
const love = (counter = 0) => {if (counter === 99) {return}console.log('The things I do for love')return love(counter + 1)}
Parameters: We'll need a counter to keep track of how many times solution has run. Let's start it at 0.
Base case: When the counter reaches 99, return. Note that because our
function calls on console.log to do something and doesn't return any value
itself, we can just put return
to end the recursion.
Recursive case: If the counter is still below 99, print 'The things I do for love', bring the counter one step closer to 99, and go again.
It might look strange to see return love(counter + 1)
in the recursive
case, but only return
in the base case. Shouldn't a function always either
have a return value or not?
Each of the 99 calls to love
will pass its return value to the love
that
called it, and saying return
is like saying return undefined
. So the very
last call will pass an undefined
return value all the way back to first
call, and the first call will end up returning undefined
.
Did you start at 99 and count down to 1? That’s OK too. Sometimes there is more than one way to solve a recursive problem. Just be careful, because sometimes counting down vs. up can change the final output of a recursive function. The next exercise is one example where the direction you count can completely change the result!
Write a function named countTo98 that calls console.log
once for every
number from 0 to 98.
countTo98()/*012...98*/
Explanation: There are no parameters and no return values, so we don't need to provide any examples
Shape
const countTo98 = () => {}
Explanation
i
) that starts at 0 and keeps track of
which number we are on.i
is greater than 98, returni
Code
const countTo98 = (i = 0) => {if (i > 98) {return}console.log(i)return countTo98(i + 1)}
Write a function named countToMe that calls console.log
for every number
from 8 to the input number.
countToMe(10)/*8910*/
Examples
// Example 1countToMe(-4)/**/// Example 2countToMe(0)/**/// Example 3countToMe(8)/*8*/// Example 4countToMe(13)/*8910111213*/
Shape
const countToMe = num => {}
Explanation
i
) that starts at 8i
is greater than num, returni
Code
const countToMe = (num, i = 8) => {if (i > num) {return}console.log(i)return countToMe(num, i + 1)}
Write a function named fizzbuzz that call console.log
for every number from
1 to the input number. However, if the number is divisible by 3, log "fizz"
instead and if the number is divisible by 5, log "buzz"
instead. If the
number is divisible by both, log "fizzbuzz"
.
fizzbuzz(16)/*12fizz4buzzfizz78fizzbuzz11fizz1314fizzbuzz16*/
Examples
//Example 1fizzbuzz(-3)/**///Example 2fizzbuzz(0)/**///Example 2fizzbuzz(3)/*12fizz*///Example 2fizzbuzz(5)/*12fizz4buzz*/
Shape
const fizzbuzz = num => {}
Explanation
i
) that starts at 0.i
is greater than num
, return.val
) to store what the output should
be at each num
. Initial value is i
i
is divisible by 3, assign fizz
to val
.i
is divisible by 5, assign buzz
to val
.i
is divisible by 3 and 5, assign fizzbuzz
to val
.val
Code
const fizzbuzz = (num, i = 1) => {if (i > num) {return}let val = iif (i % 3 === 0) {val = 'fizz'}if (i % 5 === 0) {val = 'buzz'}if (i % 3 === 0 && i % 5 === 0) {val = 'fizzbuzz'}console.log(val)return fizzbuzz(num, i + 1)}
Write a function named numberedHello
that returns a string with the same
number of "hello"
as a given number.
numberedHello(5) // "hellohellohellohellohello"numberedHello(0) // ""numberedHello(-4) // ""
Examples
//Example 1const a = numberedHello(0) // a is ""//Example 2const b = numberedHello(-3) // b is ""//Example 3const c = numberedHello(2) // c is "hellohello"
Shape
const numberedHello = num => {}
Explanation
result
) that stores an empty string ""
initially.num
is less than 1, return result
"hello"
to result
num
and passing in "hello" + result
into the
next function.Code
const numberedHello = (num, result = '') => {if (num < 1) {return result}return numberedHello(num - 1, result + 'hello')}
Parameters: Of course we need a counter like in the previous examples. But this time we'll also need a result variable to store all the hellos. This is just like exercise 2 in the Closure section, except that now we're using recursion. Our result will start as an empty string "", and our recursive function calls can pass it to each other to build onto.
Base case: When the counter reaches 0, return the result variable with
all the hellos. (Note that we said if ( num < 1 )
. What would happen if we
wrote if ( num === 0 )
and someone called solution(-1)
?)
Recursive case: Decrease the counter and add one "hello" to the result as we call this function again.
Write a function named sumEvens
that adds up all the positive even numbers
from 2 to the given number. (Use %
to determine if each number is even.)
let result = sumEvens(5) // result is 6 because 4 + 2result = sumEvens(1) // result is 0
Examples
//Example 1const a = sumEvens(-8) // a is 0//Example 2const b = sumEvens(0) // b is 0//Example 3const d = sumEvens(2) // d is 2//Example 4const c = sumEvens(10) // c is 30 : (2 + 4 + 6 + 8 + 10)
Shape
const sumEvens = num => {}
Explanation
result
) that has a value of 0 initially.num
is less than or equal to 0, return.num
is even, add it to result
.num
Code
const sumEvens = (num, result = 0) => {if (num <= 0) {return result}if (num % 2 === 0) {result = result + num}return sumEvens(num - 1, result)}
Write a function named tryNumRange
that takes in a number and a function
and calls the function with every number from 1 to the input number. If any
of these return true, return true; if they all return false, return false.
let res = tryNumRange(15, e => {return e > 10}) // res is true, because the input function returns// true when it is called with 11res = tryNumRange(8, e => {return e === 19}) // res is false, because passing 1-8 into// the input function will never return true
Examples
//Example 1let res = tryNumRange(0, e => {return e + 10 > 20}) // res is false since range does not include 0//Example 2res = tryNumRange(-2, e => {return e % 2 === 0}) // res is false since negative numbers are not included in the range//Example 3res = tryNumRange(8, e => {return e === 19}) // res is false, because passing 1-8 into// the input function will never return true//Example 4res = tryNumRange(8, e => {return true}) // res is true
Shape
const tryNumRange = (num, fun) => {}
Explanation
counter
) that starts at 1.counter
is greater than num, return false.fun
with counter
as argument is truthy, return true.Code
const tryNumRange = (num, fun, counter = 1) => {if (counter > num) {return false}if (fun(counter)) {return true}return tryNumRange(num, fun, counter + 1)}
Look at the code below and try to figure out how to use fun1
. Next, write
some code that calls fun1
.
// fun1 runs b (2nd argument) x timesfun1(99, () => {console.log('hello')})
Examples
//Example 1const a = fun(2, () => {console.log("c0d3")})/*"c0d3""c0d3"a is undefined since there is no value being returned.It prints out string 'c0d3' 2 times.
Shape
const fun1 = (x, b) => {}
Explanation
x
a function b
x
is less than or equal to 0, returnb
functionCode
const fun1 = (x, b) => {if (x <= 0) {return}b()return fun1(x - 1, b)}
Now that you know recursion, let's learn more about strings so you can tackle the harder string problems.
A JavaScript string stores a series of characters like "John Smith". A string can be any text inside double or single quotes:
const a = "iPhone 11"
const b = 'iPhone 11'
String indices are zero-based: The first character is in position 0, the second in 1, and so on.
To access a character in a string, you can use square brackets with the
position. For example, "hello"[1]
will give you the character "e"
in the
second position.
Strings also have a special property called length
. For example,
"hello".length
will give you 5 which is the length of "hello".
Write a function named solution that takes in a string and calls console.log()
for every letter in the string.
3 Examples - Let's make sure we understand the problem!
//Example 1const a = logString('') // returns undefined since we are passing in empty string as argument.//Example 2const c = logString(' ') // c logs empty space/*" "(empty space)*///Example 3const b = logString('Hello') // b is/*Hello*/
Shape
const logString = str => {}
Explanation
i
) that starts at 0 and keeps track of
where we are in str
i
is equal to str.length
), return.str[i]
Code
const logString = (str, i = 0) => {if (i >= str.length) {return}console.log(str[i])return logString(str, i + 1)}
Debrief
Not too bad, right? Ready to try some on your own?
Write a function named logNonMatching
that console.log
s every character
in a word, except for one given character.
logNonMatching('banana', 'a')// Will print out everything not matching "a":/*bnn*/
Examples
//Example 1logNonMatching('', 'c') // output is ""//Example 2logNonMatching('I love computer science!', ' ')// removes empty space/*Ilovecomputerscience*///Example 3logNonMatching('ccc', 'c') // output is ""
Shape
const logNonMatching = (str, character) => {}
Explanation
i
) that starts at 0 and keeps track of
where we are in str
.i
equals str.length
(we are at the end) return.str[i]
doesn't equal character
, console.log str[i]
Code
const logNonMatching = (str, character, i = 0) => {if (i === str.length) returnif (str[i] !== character) console.log(str[i])return logNonMatching(str, character, i + 1)}
Write a function named logFirstX
that logs a given number of characters
from the beginning of a string.
logFirstX('Winterfell', 3)// Will print out (3 characters):/*Win*/
Examples
//Example 1const a = logFirstX("", 4) // logs "" since there is nothing to log//Example 2const b = logFirstX("Happy", 10) // logs the whole string/*Happy*///Example 3const c = logFirstX("I am having a great day!", 5)/*I" "(empty space)am" "(empty space)/*
Shape
const logFirstX = (str, num) => {}
Explanation
i
) that starts at 0 and keeps track of
where we are in str
.i
is ≥ num
or i
is ≥ str.length
, returni
Code
const logFirstX = (str, num, i = 0) => {if (i >= num || i >= str.length) {return}console.log(str[i])return logFirstX(str, num, i + 1)}
Write a function named lastX
that returns (not console.log) a chunk of a
given size from the end of a string.
lastX('Winterfell', 3) // returns "ell"
Examples
// Example 1const b = lastX('', 4) // b is ""// Example 2const a = lastX('hello', -11) // a is ""// Example 3const d = lastX('a', 1) // d is "a"// Example 4const c = lastX('Winter', 8) // c is "Winter"
Shape
const lastX = (str, num) => {}
Explanation
i
) that starts at the last character index:
str.length - 1
result
) that starts with "" to collect all
the characters.result
's length is ≥ to num
or if i
becomes smaller than 0, we
know we are done so return result
str[i]
Code
const lastX = (str, num, result = '', i = str.length - 1) => {if (result.length >= num || i < 0) {return result}return lastX(str, num, str[i] + result, i - 1)}
Write a function named logOddOnly
that prints out only the characters at
odd indices (remember, index starts at 0) from a string.
logOddOnly('catelyn')// Will print out (3 characters):/*aey*/
Examples
//Example 1logOddOnly("")// Will print out nothing//Example 2logOddOnly("I love coding!")// Will print out (7 characters):/*(empty space)oecdn!*///Example 3logOddOnly(" This is fun! ") // "Ti sfn "// Will print out (7 characters):/*Tisfn(empty space)*/****
Shape
const logOddOnly = str => {}
Explanation
i
) that starts at 0 and keeps track of
where we are in str
.str
, return.i
is odd and if it is, we print out str[i]
.Code
const logOddOnly = (str, i = 0) => {if (i === str.length) returnif (i % 2 !== 0)// A quicker way is to simply do i % 2console.log(str[i])return logOddOnly(str, i + 1)}
Write a function called removeCharacter
that returns a string without any
instances of a given character.
removeCharacter('banana', 'a') // bnn
Examples
// Example 1const a = removeCharacter(' ', 'abc') // a is ''// Example 2const b = removeCharacter('Jump', 'i') // b is "Jump"// Example 3const c = removeCharacter('Iblovebcoding!', 'b') // c is "Ilovecoding!"
Shape
const removeCharacter = (str, letter) => {}
Explanation
i
) that starts at 0 and keeps track of
where we are in str
.acc
) that starts with ""
and we add to
it.acc
acc
(if str[i]
doesn't match letter
)Code
const removeCharacter = (str, character, i = 0, acc = '') => {if (i === str.length) {return acc}if (str[i] !== character) {acc = acc + str[i]}return removeCharacter(str, character, i + 1, acc)}
Write a function named secretCodeGenerator
that takes in 3 parameters: a
string, a letter, and a string, and returns a string where all the matching
letters are replaced by the last input string.
secretCodeGenerator('banana', 'a', '*z*') // b*z*n*z*n*z*
Examples
// Example 1: nothing to replacesecretCodeGenerator('', 'b', ' ')// returns ''// Example 2: no matches to replacesecretCodeGenerator('acd', 'b', 'hello')// returns 'acd'// Example 3: Removes b and add space " " instead.secretCodeGenerator('Ibambhappy', 'b', ' ')// returns 'I am happy'// Example 4: Replaces "o" with "u"secretCodeGenerator('Boomerang', 'o', 'u')// returns 'Buumerang'
Shape
const secretCodeGenerator = (str, letter, replacement) => {}
Explanation
i
) that starts at 0 and keeps track of
where we are in str
acc
that starts with ""
and we add to it character
by characterstr[i]
matches letter, we add the replacement.acc
acc
(could be str[i]
or replacement
)Code
const secretCodeGenerator = (str, letter, replacement, i = 0, acc = '') => {if (i >= str.length) {return acc}if (str[i] === letter) {acc = acc + replacement} else {acc = acc + str[i]}return secretCodeGenerator(str, letter, replacement, i + 1, acc)}
This section will cover the asynchronous nature of JavaScript that differentiates it from other languages. To understand it, let's explore a hypothetical scenario:
If you are baking pizza and you put the pizza into the oven, do you wait until the pizza is done before doing something else? Probably not. You will probably do other things and and come back to it after the oven finishes cooking the pizza. Usually, the oven will let you know when it is done (like emitting a beep).
JavaScript works the same way. In the following example, setTimeout
(you might
remember this function from Preflight) takes in 2 arguments, a function and a
time (in milliseconds). The function will run after the milliseconds have
passed.
let points = 0setTimeout(() => {points = 5}, 1000) // function will run after 1000 milliseconds (or 1 second)let res = points // res is 0.// JavaScript keeps going and does not sit around and wait.// How do you fix the code so res takes the value of points after points is updated?
let points = 0let res = 0setTimeout(() => {// note that this function (which will run after 1000ms)// is doing 2 things:// 1. setting points to 5// 2. setting res to points.points = 5res = points}, 1000)
let points = 2setTimeout(() => {if (points < 5) {console.log(points) // will console.log be called?}}, 1000)points = 10
No, since JavaScript does not wait around, points = 10 executes before the callback function.
callbacks are functions that are passed into another function as arguments.
console.log("one")
.Examples
wait20() // after 20 seconds, "one" will be printed out.
Function Shape
const wait20 = () => {}
Explanation
setTimeout
, which takes in a function and a number (20
seconds)console.log
Code
const wait20 = () => {setTimeout(() => {console.log('one')}, 20 * 1000) // setTimeout uses milliseconds. 1000 milliseconds per second}// FYI. wait20 will return undefined, because it has no return
oneAndTwo
that waits 20 seconds and then calls
console.log("one")
, then waits another 10 seconds and then calls
console.log("two")
. Reminder: 'two' will appear 30 seconds after the
function is run.Examples
oneAndTwo()/*... 20 seconds later ..."one"... 10 seconds later..."two"*/
Function Shape
const oneAndTwo = () => {}
Explanation
setTimeout
, and pass in 2 arguments, a function and 20000console.log
setTimeout
with 2 arguments, a function and 1000console.log
Code
const oneAndTwo = () => {setTimeout(() => {console.log('one')setTimeout(() => {console.log('two')}, 10 * 1000)}, 20 * 1000) // setTimeout uses milliseconds. 1000 milliseconds per second}
console.log
for every
character in the string, 1 second after each call (aka 1 character per
second).Examples
// Example 1printLetter('')/**/// Example 2printLetter('hello')/*... 1 second later ..."h"... 1 second later..."e"... 1 second later..."l"... 1 second later..."l"... 1 second later..."o"*/
Function Shape
const printLetter = str => {}
Explanation
i
to keep track of which letter we are on, starting
from 0i === str.length
, stop / returnsetTimeout
and pass in a function and 1000printLetter
with i+1
.Code
const printLetter = (str, i = 0) => {if (i === str.length) {return}setTimeout(() => {console.log(str[i])printLetter(str, i + 1)}, 1000)}
Function on the left will completely stop in about.... 1 second. All the letters gets printed out all at once after about 1 second, no matter how long the string is.
Please solve these challenges with recursive functions instead of using for
or
while
loops. Some of these challenges do not need a recursive solution, but if
you feel the need to use for
or while
, then please use recursion instead.
Complete JS1 challenges