DEV Community

Discussion on: How to approach solving a challenge during a coding interview

Collapse
 
gypsydave5 profile image
David Wickes

Hi Ady! Can I leave you some comments?

Tests. There are no tests around this code. I'm not saying you need to do test-driven development. But if you're going to perform refactoring on a codebase it's important that you're covered by tests that will tell you whether you've introduced a bug while you're ~showing off~ cleaning up the code.

You could argue that this is unimportant for something as trivial as Fizz Buzz - you can easily perform manual testing of your single function with a few select cases. But I would say that, during a coding interview, it's really important to treat the problem with the same respect that you'd give to any program you'd write professionally. It's a real opportunity to show off how you like to work and how you think.

A few test cases, which can be run quickly, can easily demonstrate the interviewer that you've not introduced any regressions in your code.

Other than that, I like where you took the code in the end (but I think there's an even more ridiculous iteration you could get to in one line).

Collapse
 
adyngom profile image
Ady Ngom

You bet you can leave comments David and what a great contribution.

Regarding TDD I think it can definitely be something to ask at the top of the exercise, you would be surprised that some of the companies would tell not be too worried about it in the interview context but will definitely like that you are thinking that way.

Most of the times, the TDD aspect will be raised as a follow-up question regarding quality and maintainability. I even had many scenarios of where the setup will be a stub function with failing tests and the exercise was to make the tests pass and add more if needed.

I totally agree that, as an interviewer, I would highly think of a candidate who talks and write tests from the get-go. I will probably add a section in the article of what it could look like.

Now regarding one liners :)
I actually have come up with about three of them while I was deep diving, and I still came back to the final solution of the article for a few reasons but mainly for:

  • intuitiveness: I can scan real quick and see the logic and have an idea of what's going on
  • readability: I did not squint
  • no significant gain: either in performance or anything else since dev code gets minified anyways before prod

For the one-liners, even I was the one who came up with the solution I still needed a minute to remember how I got there so I can only imagine if someone else was to review my code.

I agree that it is subjective and might be about coding culture and style.

Please add the fix for the iteration. I'll be happy to add it as an option to the article.

Cheers

Collapse
 
johnboy5358 profile image
John

One last visit to fizzBuzz re: making output testable and creating small testable pure functions.

Here, I've created a reuseable predicate function isMultOf, which is then used it to make two further predicate functions isMultOf3 and isMultOf5. These are then combined to make the conjunction predicate isMult3And5. Finally, a range function generates the array of integers required.


// curried isMultOf
const isMultOf = a => b => !(b % a)
// from isMultOf make the two predicates we need.
const isMultOf3 = isMultOf(3)
const isMultOf5 = isMultOf(5)

// and the conjuction of the two preds. above.
const isMult3And5 = n => isMultOf3(n) && isMultOf5(n)

// an inclusive range function.
const range = (start, end) => Array.from({length: (end - start + 1)}, (_, i) => i + start)


const fizzBuzz = (s = 1, e = 100) =>
  range(s, e)
    .reduce((p, c) =>
      isMult3And5(c)
        ? p + 'FizzBuzz\n'
        : isMultOf3(c)
          ? p + 'Fizz\n'
          : isMultOf5(c)
            ? p +'Buzz\n'
            : p + c + '\n', '')

console.log(fizzBuzz(1, 30))

/*
  => `1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz
16
17
Fizz
19
Buzz
Fizz
22
23
Fizz
Buzz
26
Fizz
28
29
FizzBuzz
`
*/

:-)

Collapse
 
johnboy5358 profile image
John

Hi Ady,

With regards testing and your final code for fizzBuzz...


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);


Since testing was mentioned previously, I would like to hear more about your strategy for testing functions that do not have a useful return value (fizzBuzz returns undefined; output is only via console.log).

Perhaps, a follow up post to cover this would be interesting.

Thanks for the post.

Thread Thread
 
adyngom profile image
Ady Ngom

Hey John nice to hear from you. Yes will certainly do a follow up and just today someone was asking testing during a lecture.
Will address it aASAP.
Cheers

Thread Thread
 
johnboy5358 profile image
John

Thanks Ady, I will await your future post on the subject, but in the meantime, I can see a way that fizzBuzz could be tested although it would need some modification from it's present form.

Thread Thread
 
adyngom profile image
Ady Ngom

Great please do share your solution if you do not mind. As I mentioned the one proposed is a personal preference and there are many other ways to solve for it.
Testing for it should also be pretty straightforward. If console.log is an issue we could build and return an array of outputs in the function and write a test for small subsets.

Thread Thread
 
johnboy5358 profile image
John

That sounds more interesting than the approach I was thinking of, I would prefer to follow your lead, so could you expand on your idea of an array of outputs and testing small subsets?

Thread Thread
 
adyngom profile image
Ady Ngom

Hello John, below I'm adding what I would consider a more 'unit testable' solution to the FizzBuzz challenge.

It is important to note that this steps out of the context of solving a challenge during a coding interview and jumps into the refining and 'make ready for prod' exercises.

Like John Papa says in his talk 'Readable Code' - "Nothing start readable. Write dirty code, then clean it".

I would argue the same for testable in the context of a coding exercise.

const output = arrayToString(createArrayFromRange(1, 100).map(fizzBuzz));

console.log(output);

function createArrayFromRange(start = 1, end = 1, increments = 1) {
    return [start, ...Array.from({ length: end - 1 }).map(() => start += increments)];
}

function arrayToString(arr = [], separator = ', ') {
    return arr.join(separator);
}

function fizzBuzz(number) {
    let output = ((number % 3) ? '' : 'Fizz');
    output += ((number % 5) ? '' : 'Buzz');
    return output || number;
}

The above provides you functions as very isolated units making them easily testable. Now this does not address writing tests for void functions or methods. Depending on the suite you use, there might be different strategies such as using spy if you write with Jasmine.

That should be for an entire different post altogether :) Let me know what you think.

Cheers

Thread Thread
 
johnboy5358 profile image
John

Yep, looks pretty good to me. Now it's returning a value it's testable.

Just for some added value I've included a small function called should to test it with and, of course, it passes...


// small unit testing function follows...
const eq = (a, b) => a === b
// curried should function.
const should = fn => (t, s) => (fn(t, s)) ? 'passed' : 'failed'
const shouldEq = should(eq)

const fb1To15 = "1, 2, Fizz, 4, Buzz, Fizz, 7, 8, Fizz, Buzz, 11, Fizz, 13, 14, FizzBuzz"

// Test fizzBuzz first 15 terms.
console.log(
  `Assert: First 15 terms of FizzBuzz should equal:
    1, 2, Fizz, 4, Buzz, Fizz, 7, 8, Fizz, Buzz, 11, Fizz, 13, 14, FizzBuzz:
    Test: ${shouldEq( arrayToString(createArrayFromRange(1, 15).map(fizzBuzz)), fb1To15 )}
  `
)
/*
 => Assert: First 15 terms of FizzBuzz should equal:
    1, 2, Fizz, 4, Buzz, Fizz, 7, 8, Fizz, Buzz, 11, Fizz, 13, 14, FizzBuzz:
    Test: passed
*/

// End test.

const output = arrayToString(createArrayFromRange(1, 100).map(fizzBuzz));
console.log(output);

function createArrayFromRange(start = 1, end = 1, increments = 1) {
  return [start, ...Array.from({ length: end - 1 }).map(() => start += increments)];
}

function arrayToString(arr = [], separator = ', ') {
  return arr.join(separator);
}

function fizzBuzz(number) {
  let output = ((number % 3) ? '' : 'Fizz');
  output += ((number % 5) ? '' : 'Buzz');
  return output || number;
}

My only other suggestion is you could do the following to get back to your original fizzBuzz implementation.


function FizzBuzz(s = 1, e = 100) {
  return arrayToString(createArrayFromRange(s, e).map(fizzBuzz));
}

Cheers Ady ... It's just gone midnight here, so better get some sleep.

Bye for now.

Thread Thread
 
adyngom profile image
Ady Ngom

As always great contributions John. I really like the idea of a small assertion library to quickly test code inline and not having to rely on a full blown testing suite. I have in fact started a project on github called dotLog to do just that. I haven't had the chance to do more on it lately but it would be fun to have you contribute to it and maybe have a few other people do.
Good night for now, let me know what you think about that in whatever your AM is.
Cheers :)

Thread Thread
 
johnboy5358 profile image
John

Yes, when you don't want the overhead of a full blown test suite a quick and dirty should function will do. I know it pretty minimal, but, sometimes, less is best!

I had a quick look at your dotLog code, but have'nt got much time this morning to figure it out. To aid my understanding, could you update the Readme with an example of usage, eg.


dotLog.describe()
// takes in a String and an Array of Objects?
// What properties does each object have?
// etc, etc.

I don't want to guess at it.

Also, I have'nt played about much with the finer points of console.log, but I think the syntax that you use in dotLog will only work in the browser, but not when used with node. However, node's console.log will do colour changes, but I think the syntax is a little different. This would be worth exploring to make dotLog useable in the browser and node. Correct me if I'm writing crap here.

I will take another look this evening and otherwise wait for the Readme update. Yeh! Could be fun, but, as you know, I do have a tendency towards a functional style of JS? But, whatever, I do love JS

Cheers Ady.

Thread Thread
 
adyngom profile image
Ady Ngom

Yes you are absolutely right - you should not have to try to figure it out. I need to do an initial clean up and set it in a way that it's intuitive and easy to contribute.
I don't mind the functional approach 'au contraire', I have been writing a lot like that lately. Point free composition is an absolute gem when done right. I just want to steer away from getting into dogma stands with FP vs OOP. I really think they can be complementary.
I've learned a lot from your style of writing and started implementing some of it. I could not handle professor Frisbee though, my head was spinning with that voice after a while lol.
Anyhow I'll clean up the dotLog project and ping you as soon as it's ready. Probably sometimes next week since this one is going to be crazy on my side.
Cheers :)

Thread Thread
 
johnboy5358 profile image
John

Yes, absolutely with you on that; fp, oop => it's all JS to me!

Yes, again Prof. Frisbee (aka Brian Lonsdorf) I've watched a number of his talks on youtube and I think he is a great guy to learn from, but making something funny (as he did with Prof. Frisbee) can always risk a "Marmite" response... you either love it or hate it!

No rush on the dotLog project... it's for fun; no deadlines required!

So long for now Ady

Thread Thread
 
johnboy5358 profile image
John • Edited

Hey Ady,

I've been investigating dotLog. I just wanted to let you know how much I like it. dotLog is minimal, that's nice; personally, I would keep it that way. Start a new project if you want more features. Here's what I've had time to suss so far...


const concatS = add = (a, b) => a + b

dotLog.describe("function add", [
  { 
    label: "add is a function",
    rule: ( typeof add === 'function')
  },
  {
    label: "add takes two arguments",
    rule: (add.length === 2)
  },
  { 
    label: "add - Adds two numbers together; add(5, 4) returns 9",
    rule: (add(5, 4) === 9)
  },
  {
    label: "Add should return a value of type number, if passed two numbers",
    rule: (typeof add(5, 4) === 'number')
  }
])

dotLog.describe("function concatS", [
  { 
    label: "concatS is a function",
    rule: ( typeof concatS === 'function')
  },
  {
    label: "concatS takes two arguments",
    rule: (concatS.length === 2)
  },
  { 
    label: "concats two strings together; concatS('Super', 'man') returns 'Superman'",
    rule: (concatS('Super', 'man') === 'Superman')
  },
  {
    label: "concatS should return a value of type String if passed two strings",
    rule: (typeof concatS('Super', 'man') === 'string')
  }
])


Keep up the good work Ady. I will be using dotLog.

:)

AND THERE IS MORE! How about dotLog described by dotLog


dotLog.describe("dotLog", [
  {
    label: "dotLog is an Object",
    rule: (typeof dotLog === 'object')
  },
  {
    label: "dotLog has two properties",
    rule: (Object.keys(dotLog).length === 2)
  },
  {
    label: "dotLog.describe is a function",
    rule: (typeof dotLog.describe === 'function')
  },

  // {pending tests here to get it working in node.js console},
  // { and so on, and so on. I think you get the idea! }
])

Of course, I'm just kidding. You really should'nt have something that is'nt tested, testing itself! But it was just a moment of madness.

All the best :-)

Thread Thread
 
adyngom profile image
Ady Ngom

You are so simply the best mate!!! And that’s very motivating that you could already find use for it.
You just became the official first adopter lol.
Thanks and let’s keep at it.
Cheers

Thread Thread
 
johnboy5358 profile image
John • Edited

You keep at it mate!

Remember, take your time, don't rush it. Anything worth doing is worth doing well.

:-)

Catch you later.

PS.

Incidentally, when I first took a quick look at the dotLog code I did'nt have much time, but I noticed within the .describe method you were mapping over the testSet array and also that testSet contained objects with two properties label and rule; but still the "penny had not dropped" (ie. I had not got that aha moment). When I looked again at dotLog, some hours later, I then found your .png file(console-test-call.png) and instantly new what was going on.

Now, I'm just telling you this so that you know how important the example you provided in console-test-call.png was to understanding your code.

I think that, for me atleast, was something worth remembering.

Thread Thread
 
johnboy5358 profile image
John

Hey Ady,

Just thought I'd give you a heads up on a new log function I propose for dotLog. It basically addresses the issue of logging to nodejs. ie. after the environment that dotLog is running in has been detected, it selects the correct syntax for console.log in node or browser.

I've put it up on repl.it just for a quick preview.

repl.it/@johnboy5358/dotLog

cheers :-)

Thread Thread
 
adyngom profile image
Ady Ngom

Great I think we have similar ideas on that. I looked at a package called colors yesterday for the node side of things. The next step was to find a way to detect the environment and to do the appropriate output.
I’m glad you beat me to it 😀

Thread Thread
 
johnboy5358 profile image
John

Yes, I saw the colors package, but thought it might be a case of a "sledgehammer to crack a nut"; so decided not to. However, Ady, dotLog is your project and of course you should always have the final say since only you know which way you want to take it.

:-)

Thread Thread
 
adyngom profile image
Ady Ngom

No no it's not mine it's open source lol :) The best approach should prevail. I like the simplicity of what you have put together. When I did my initial research colors was the first option that came up. The idea will be to have something as lean as possible.
Please feel free to suggest alternatives and may the one that makes most sense win. It will be of great benefit if you feel entirely enabled to make or suggest whatever you see fit. Cheers :)