How to approach solving a challenge during a coding interview

How to approach solving a challenge during a coding interview

·

10 min read

Like many things in life, mastery requires practice and the coding interview is no exception. Often times though, the focus is on trying to find the best solution from the get go rather than the approach on how to iterate and maybe get there.

The most important part, in my humble opinion is to get it right first. In your normal coding life you would rarely, if ever, be able to produce "perfect" code without first going through a set of iterations.

The approach during the coding interview should not differ and if played well should help you score invaluable points into demonstrating your problem solving skills.

I'm going to walk you through of what could be an actual conversation between you and an interviewer if you were asked to solve for the [in]famous FizzBuzz challenge.

The challenge

Write a program that prints the numbers from 1 to 100. But for multiples of three print Fizz instead of the number and for the multiples of five print Buzz. For numbers which are multiples of both three and five print FizzBuzz

The context

The FizzBuzz challenge is not specific to JavaScript and has been part of the coding interview process in almost every programing language. It is usually a quick check to assess the candidate basic programing instincts, but can also be turned in an assessment for in depth knowledge if the interviewer decides to do so. It is usually part of a light weight first technical interview done while screen sharing. It is also a favorite from a non JavaScript programmer to ask and quickly gauge your technical knowledge and approach.

In a Javascript context familiarity with some or all the following concepts are expected to be demonstrated:

  • Logical operators
  • Looping
  • Falsy values
  • Ternary operator
  • Type coercion

The approach

As with any problems that you might encounter, even those that seem familiar, a good read and break down to small pieces is a must. Be clear to the interviewer that you need 3 to 5 minutes to read it calmly and propose a rewrite of your understanding.

If you are comfortable doing that part out loud, that's even better. For example this is how I might go for the rewrite:

  • So log to the console numbers from 1 to 100 - I'm going to need a loop
  • For multiple of 3 instead of the number output the string 'Fizz'
  • Do the same for multiples of 5 with the output being 'Buzz'
  • In the case the number is a multiple of both 3 and 5 then output 'FizzBuzz' - how to check if a is a multiple of b??
  • If all the above cases fail then just output the number as is

I would probably ask to the interviewer if I should worry about edge cases or bad inputs. It is usually implied that the input will be correct and edge cases might not be necessary. The fact that you ask though, adds a touch of eloquence to your problem-solving approach.

The solution(s)

One thing that is key and is worthy of practice is walking the person through your steps as you are building the solution during the interview. Start with the obvious, you will probably need a function or class as your primary construct. Start there and always think of the K.I.A.S.S.A.P :) principle - Keep It As Stupid Simple As Possible

First step

// comments are me talking out loud
// let's build the function structure
function fizzBuzz( start = 1, end = 100) { // default parameters to set the default range
    // I need a loop - let's go with for
    for( let i = start; i <= end; i++) {
        // probably a variable for what will be outputted
        let output = i;

        // rest of the logic here

        // outputting the result
        console.log(output);
    }
}
// call the function
fizzBuzz(); // this prints out 1 to 100 - fancy ;)

The above satisfies my first goal on my rewritten challenge understanding

Second step

Now if I follow the cadence of the challenge I will solve for two things:

- Choosing the proper operator to find if a number is a multiple of another
- Apply it for the multiple of 3 condition and output 'Fizz'

The remainder operator - %, is the perfect tool here. If number a is a multiple of number b then

( b % a) === 0; // will be true;
// 4 is a multiple of 2
( 4 % 2 ) === 0; // is true

Let's apply this in the body of our function

// rest of the logic here
if( (i % 3) === 0 ) {
    output = 'Fizz';
}
// Knowing that 3,6 and 9 are multiple of 3 let's
// quickly test a small sequence by calling

fizzBuzz(1,10); 
// this should output
// 1, 2, 'Fizz', 4, 5, 'Fizz', 7, 8, 'Fizz', 10

Final step

Since the Fizz condition ran perfect we can now apply the same logic to the rest

// multiple of 5
if( (i % 5) === 0 ) {
    output = 'Buzz';
}

// multiple of 3 and 5
if( (i % 3) === 0  && (i % 5 === 0)) {
    output = 'FizzBuzz';
}

Wowza!! this satisfies all the conditions and gives us this chef d'oeuvre of a solution once assembled and stripped out of all comments

function fizzBuzz( start = 1, end = 100) { // default parameters to set the default range
    for( let i = start; i <= end; i++) {
        let output = i;
        if( (i % 3) === 0 ) {
            output = 'Fizz';
        }
        if( (i % 5) === 0 ) {
            output = 'Buzz';
        }
        if( (i % 3) === 0  && (i % 5) === 0) {
            output = 'FizzBuzz';
        }
        console.log(output);
    }
}
fizzBuzz();

Now at this point, I have a working solution that satisfies the challenge request. What follows is very delicate in an interview situation. Something is bugging me about my code. The last if block that checks for multiples of 3 and 5 seem redundant.

Now should I voice that out loud and propose to refactor it or should I wait for the interviewer to call it out?

Interviews are about managing time and maximizing your pluses over your minuses. If you feel super confident that you have a good shot at coming up with something more solid in a manageable time then go for it. If in doubt, wait to be asked.

This way, the interviewer has decided that the remainder of your time might be worth digging deeper on this question.

If it is decided that it would be interesting to look at a refactor, this might be a way to approach the refactor steps

The refactor

We could, of course, get to a fancy one-liner here for this particular challenge, but I'm not a particular fan of doing stuff for the sake of fancy or pretty.

So let's flip the switch what I'm going to do this time is I will show you my final solution and I will walk you through how did I get to it.

This can turn into a handy skill if you are to read and understand other people's code or if you are to explain it to someone else. Through the years I have provided many solutions for this challenge, but the one below is by far my favorite.

function fizzBuzz( start = 1, end = 100) {
    for( let i = start; i <= end; i++) {
        let output =  ( (i % 3) ? '' : 'Fizz' ); // if multiple of 3 is falsy
        output += ( (i % 5) ? '' : 'Buzz') ; // if multiple of 5 is falsy
        console.log(output || i); // output value or i if output is falsy
    }
}
fizzBuzz(1,15);

The solution uses the ternary operator syntax to set the conditions and takes advantage of something that might not very obvious at first for the untrained eye - JavaScript falsy values.

Let's start with falsy values JavaScript, what in the heck are we talking about. A great definition is provided by the Mozilla Developer Network (MDN ):

A falsy value is a value that is considered false when encountered in a Boolean context. JavaScript uses Type Conversion to coerce any value to a Boolean in contexts that require it, such as conditionals and loops.

For our particular context the important keywords are "Boolean context" and "conditionals" since they are relevant to our solution. Before looking at how it applies, here is the list of the most common falsy values in Javascript:

  • The boolean false not the same as the string 'false'
  • The number 0 - once again this is different from the string '0'
  • The null object
  • The primitive type undefined assigned to a non-initialized variable
  • Any representation of an empty string such as a single quote, double quotes or back-ticks.

The rewrite

Let's focus on one segment of our fizzBuzz function

if( (i % 3) === 0 ) {
output = 'Fizz';
}
// this could be refactored as
if( !(i % 3) ) output = 'Fizz';

Breaking down the refactored line gives us this picture

  • if (...) ==> conditional construct outside - boolean context inside
  • ! ==> is false
  • (i % 3) ==> type coercion - will check if value is falsy or truthy

Replace i by a few numbers to better understand it

if (!( 1 % 3) ...) /*becomes*/ if (!( 3 ) ...) /*3 is not false or falsy so check fails*/
if (!( 2 % 3) ...) /*becomes*/ if (!( 6 ) ...) /*6 is not false or falsy so check fails*/
if (!( 3 % 3) ...) /*becomes*/ if (!( 0 ) ...) /*0 is not false but is falsy so check passes*/

I can rewrite now my entire function using the logic above

function fizzBuzz( start = 1, end = 100) {
    for( let i = start; i <= end; i++) {
        let output = i;
        if( !(i % 3) ) output = 'Fizz';
        if( !(i % 5) ) output = 'Buzz';
        if( !(i % 3) && !(i % 5) ) output = 'FizzBuzz';
        console.log(output);
    }
}

I was quite ecstatic when I got to this solution, but it did not too long unfortunately. The last line was still redundant to me and honestly was bugging me. How could I combine the checks of 3 and 5 in one pass.

And then it hit me, what if I could start with an empty string, attach to it the word 'Fizz' if it passes the 3 condition and attach the word 'Buzz' if it passes the 5 condition too. I drew this on a piece of paper

  • i = 1 ==> no Fizz '' ==> no Buzz '' ==> output is 1
  • i = 3 ==> yes 'Fizz' ==> no Buzz '' ==> output is 'Fizz'
  • i = 5 ==> no Fizz '' ==> yes 'Buzz' ==> output is 'Buzz'
  • i = 15 => yes 'Fizz' ==> yes 'Buzz' ==> output is 'FizzBuzz'

The ternary operator will allow assigning a value if condition checks and an alternate value if it fails in a very terse manner.

Something else became obvious, we are outputting either a string or a number while we cycling through the values of i and as we saw in a previous section an empty string is a falsy value. So how do we translate all that intelligence into working code?

The essential piece to achieve that was that the value of output was either going to be one of the possible strings 'Fizz', 'Buzz', 'FizzBuzz' or be falsy. In the falsy case i will just be passed as is.

So the final rewrite with more comments

function fizzBuzz( start = 1, end = 100) {
    for( let i = start; i <= end; i++) {
        let output =  ( (i % 3) ? '' : 'Fizz' ); // output is assigned a value or empty
        output += ( (i % 5) ? '' : 'Buzz') ; // output concatenates the next value
        console.log(output || i); // || or operator if output is falsy will show i value
    }
}
fizzBuzz(1,15);

Hopefully, you followed all of that :) This was a very satisfying solution to me since I believe it was easy to read, solved the problem and had a touch of eloquent JavaScript in it.

Final words

The coding exercise covers only one aspect of the many things that happen during the coding interview.

As I mentioned the steps and being able to deliver, regardless of the complexity of the problem, take a solid amount of practice.

Don't hesitate to use mock interviews (we will be offering some in Javascript soon but more on that later) to practice the conversational aspect of it.

I hope this was useful, share and live a comment if you please :)