In my bid to introduce myself to the programming world, I imagined the best way is by solving the popular FizzBuzz problem .

A naive and intuitive implementation:

FizzBuzz - Naive
[JavaScript]
  const fizzbuzz = (n = 100) => {
  for (let i = 1; i <= n; i++) {
    if (i % 3 === 0 && i % 5 === 0)
      console.log('FizzBuzz')
    else if (i % 3 === 0)
      console.log('Fizz')
    else if (i % 5 === 0)
      console.log('Buzz')
    else
      console.log(i)
  }
}
fizzbuzz(100)

But the programmer in me will not be happy with this solution, for following reasons:

  • There is a repetition of conditional logics.
  • Additional logics cannot be easily added to existing solution, e.g. if divisible by 7, print baz.
  • The starting value is fixed at 1.
  • I would prefer to return values rather than `print` in the function.

FizzBuzz - Better
[JavaScript]
  
const range = (start, end) => Array.from(
  { length: end - start },
  (v, k) => k + start
)

const fzReducer = i =>
  (evalued, evaluator) => evalued + evaluator(i)

const fzMapper = evaluators =>
  i => evaluators.reduce(fzReducer(i), '') || i

const fizzbuzz = (n, ...evaluators) => {
  let [a, b] = Number.isInteger(evaluators[0])
    ? [n, evaluators.shift()]
    : [1, n]

  return range(a, b + 1).map(fzMapper(evaluators))
}

const createFZEvaluator = (n, msg) => i => i % n === 0 ? msg : ''
let evaluators = [
  createFZEvaluator(3, 'Fizz'),
  createFZEvaluator(5, 'Buzz')
]

let fizzbuzzes = fizzbuzz(100, ...evaluators)
fizzbuzzes.forEach(fz => console.log(fz))

Although this code manages to counter many issues caused by the naive implementation, it still passes around a 2 arrays. Once when `range` method returns an array, second when `fizzbuzz` returns array of result. This issue can be resolved using Generators

A simple solution using Generators.

FizzBuzz - Generators
[JavaScript]
  
function* range(start, end, interval = 1) {
  for(let i = start; i < end; i = i + interval) {
    yield i
  }
}

function getFizzbuzz(getEvaluators) {
  return function* fizzbuzz(min, max) {
    let [a, b] = max === void 0 ? [1, min] : [min, max]

    for(let j of range(a, b + 1)) {
      let evaluated = ''
      for(let evaluator of getEvaluators()) {
        evaluated = evaluated + evaluator(j)
      }
      yield (evaluated || j)
    }
  }
}

const createFZEvaluator = ([n, msg]) => i => i % n === 0 ? msg : ''
const evaluators = [
  [3, 'Fizz'],
  [5, 'Buzz']
].map(createFZEvaluator)

function* getEvaluators() {
  for(let evaluator of evaluators) {
    yield evaluator
  }
}

const fizzbuzz = getFizzbuzz(getEvaluators)
let fizzbuzzes = fizzbuzz(100, 110)
for (let fz of fizzbuzzes) { console.log(fz) }