Categories
Career Programming

Programmer interview challenge 2, part 5: An elegant “watcher”-based FizzBuzz implementation in JavaScript

Silhouette of a little black dress on a dress form
Ask someone who follows fashion what they think of when the word “elegant” comes up, and they’ll often come up with the Little Black Dress.

In the previous article in this series, I put out a call for alternate implementations of the “watcher”-based FizzBuzz solution. Frank Quednau answered the call with this elegant bit of JavaScript:

const wordWatcher = (interval, word) => {
  let count = 0;
  return () => {
    count++;
    if (count === interval) {
      count = 0;
      return word;
    }
    return "";
  }
}

const fizzWatcher = wordWatcher(3, "Fizz");
const buzzWatcher = wordWatcher(5, "Buzz");

for (number of Array(100).keys()) {
  const potentialFizzBuzz = `${fizzWatcher()}${buzzWatcher()}`;
  console.log(potentialFizzBuzz ? potentialFizzBuzz : number + 1);
};

Let’s take a closer look at the code for the watcher, which is assigned a word and keeps track of when to say it:

const wordWatcher = (interval, word) => {
  let count = 0;
  return () => {
    count++;
    if (count === interval) {
      count = 0;
      return word;
    }
    return "";
  }
}
  • wordWatcher has two parameters:
    • interval: The x in “Every xth number”
    • word: The word to be output
  • It uses those parameters to customize the function that it returns: a function that when called, does two things:
    • It increments its internal counter count, and
    • returns either word (if it’s time to say the word) or an empty string.

If you find yourself writing a lot of similar code with only minor differences — or worse, cutting and pasting code, followed by typing in those minor differences — you may be looking at an opportunity to use a function like this.

If you prefer to have your functions marked with the keyword function, you can change out the arrow notation and the code will still work:

function wordWatcher(interval, word) {
  let count = 0;
  return function() {
    count++;
    if (count === interval) {
      count = 0;
      return word;
    }
    return "";
  }
}

With wordWatcher defined, creating watchers for Fizz and Buzz is easy:

const fizzWatcher = wordWatcher(3, "Fizz");
const buzzWatcher = wordWatcher(5, "Buzz");

And here’s the loop that provides the output:

for (number of Array(100).keys()) {
  const potentialFizzBuzz = `${fizzWatcher()}${buzzWatcher()}`;
  console.log(potentialFizzBuzz ? potentialFizzBuzz : number + 1);
};
  • If it’s time to say FizzBuzz, or FizzBuzz, potentialFizzBuzz will contain that string. The calls to fizzWatcher() and buzzWatcher() will also increment their internal counters.
  • If potentialFizzBuzz contains anything, its contents will be printed to the console; otherwise, the current number — which has 1 added to it because array indexes start at 0 and the FizzBuzz game starts at 1 — is printed instead.

You should check out the rest of Frank’s Gist, Fizzbuzzes in many colours, which looks at FizzBuzz solutions written in several languages.

What does it mean for code to be “elegant”, anyway?

In ordinary everyday use, elegant means “pleasingly graceful and stylish in appearance or manner.” The term has been adapted by people in problem-solving fields — science, mathematics, and yes, programming — to mean “pleasingly ingenious and simple”.

And that’s what elegant code is: pleasingly ingenious and simple. This FizzBuzz implementation is elegant because it solves the problem in just over a dozen lines, is simple and concise, and even provides some new insight into programming (the use of custom-generated functions to avoid repetition).

Here’s a good list of qualities of elegant code, courtesy of Christopher Diggins article, What is the Definition of Elegant Code?:

  • It is succinct
  • It is easy to understand
  • Each function does one well-defined task
  • It conveys the programmer’s intent
  • It reflects the problem domain
  • It is easy to modify and reuse
  • If it fails, it is easy to identify that it is has failed, where it has failed, and why it has failed.
  • Its behavior (in good and bad conditions) is easy to predict

Check out the following articles — sooner or later, you’ll be interviewed by a programmer who’ll want to know if you’ve given some thought to some of programming’s more philosophical questions, and “What does it mean for code to be elegant?” is one of them:

What’s next

FizzBuzzBazz! (or: Making FizzBuzz harder).

Previously, in the “Programmer interview challenge” series