DEV Community

Cover image for [Challenge] 🐝 FizzBuzz without if/else
Keff
Keff

Posted on • Updated on

[Challenge] 🐝 FizzBuzz without if/else

This challenge is intended for Javascript, but you can complete it with any language you like and can.


Most of us will know the FizzBuzz game/exercise and probably have done it many times. It should be a simple and straightforward exercise for most developers...

BUT can you do it without using if/else statements?


Challenge Description

Write a program that outputs the string representation of numbers from 1 to N.

But for multiples of 3, it should output "Fizz" instead of the number and for the multiples of 5 output "Buzz". For numbers which are multiples of both 3 and 5, you should output "FizzBuzz".

Curveball: You must not use if/else statements, and ideally, no ternary operator.

Example:

const n = 15;

/* 
Return:
  [
    "1",
    "2",
    "Fizz",
    "4",
    "Buzz",
    "Fizz",
    "7",
    "8",
    "Fizz",
    "Buzz",
    "11",
    "Fizz",
    "13",
    "14",
    "FizzBuzz"
  ]
*/
Enter fullscreen mode Exit fullscreen mode

I will comment my solution in a couple of days.

💪 Best of luck! 💪


Credits:
Cover Image from https://codenewbiesite.wordpress.com/2017/01/29/fizz-buzz/

Oldest comments (93)

Collapse
 
tanguyandreani profile image
Tanguy Andreani

I did that in scheme r5rs:

#lang r5rs

(define (fizzbuzz n acc)
  (define (compute n)
    (let* ((fizz (and (zero? (modulo n 3))
                      "fizz"))
           (buzz (and (zero? (modulo n 5))
                      "buzz"))
           (both (and fizz
                      buzz
                      "fizzbuzz")))
      (or both fizz buzz n)))
  (or (and (zero? n) acc)
      (fizzbuzz (- n 1)
                (cons (compute n)
                      acc))))

(display (fizzbuzz 15 '()))
(newline)
; => (1 2 fizz 4 buzz fizz 7 8 fizz buzz 11 fizz 13 14 fizzbuzz)
Enter fullscreen mode Exit fullscreen mode

As promised, no ifs.

Collapse
 
nombrekeff profile image
Keff • Edited

Thanks for sharing Tanguy!

Cool stuff! Haven't heard about r5rs, looks complicated, but cool :P

Collapse
 
tanguyandreani profile image
Tanguy Andreani • Edited

Oh it isn’t that hard, it might seem complicated because of all the parenthesis and strange keywords like cons, but it’s actually just a matter of getting used to it. It’s a fun language to learn recursion for example.

It becomes a lot simpler when you learn the patterns for building recursive functions, at one point, you don’t even look at the whole function because you just think of the parts.

I started by making a function that looks like that:

#lang r5rs

(define (fizzbuzz n acc)
  (define (compute n)
    n)
  (if (zero? n)
      acc
      (fizzbuzz (- n 1)
                (cons (compute n)
                      acc))))

(display (fizzbuzz 15 '()))
(newline)
; => (1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)

This function just returns the list of numbers from 1 to 15, that's what it does because the compute function just returns whatever is n. Then I simply filled compute with my conditions.

  • define is used to define variables and functions,
  • cons is used to build a list from a head and a queue (that's not hard to understand but it definetely requires some practice and research,
  • let* allows to define local variables
Thread Thread
 
nombrekeff profile image
Keff • Edited

What a legend! I wasn't expecting an explanation, but it's highly appreciated!

Looks interesting, and after your explanation, it does not look as complicated, I might give it a try someday💪

And yeah, most languages seem a bit overwhelming at first.

Thread Thread
 
tanguyandreani profile image
Tanguy Andreani

Thanks! Feel free to ping me for help if you try to get into it and feel stuck.

Thread Thread
 
nombrekeff profile image
Keff

Thank you! I will if I get into it :)

Collapse
 
javiersilva profile image
Javier Silva Ortiz

I don't know this language, but the (and (zero? ... sounds like ternaries to me :)

Collapse
 
tanguyandreani profile image
Tanguy Andreani • Edited

It’s not a ternary, it’s just like doing && in other languages. Now you can emulate a ternary with it, I think that was the goal of the challenge. No ifs and no ternary.

Collapse
 
jessesolomon profile image
Jesse Solomon • Edited

Thanks for the fun challenge!

I'm not sure if JavaScript's type conversion is considered cheating, but I thought it was cool and wanted to share!

const n = 15;

let output = new Array(n).fill(null);

output = output.map((_value, index) => {
    let offsetIndex = index + 1;

    return (["", "Fizz"][!(offsetIndex % 3) + 0] + ["", "Buzz"][!(offsetIndex % 5) + 0]) || offsetIndex;
});

console.log(output);
Collapse
 
ogrotten profile image
ogrotten

why _value?

I understand that the 'convention' for _ is for private, but is there some other use for it here?

Or is it just habit 😂

Collapse
 
savagepixie profile image
SavagePixie

It is also a convention for unused parameters.

Collapse
 
nombrekeff profile image
Keff

Thanks for sharing Jesse!! It's a really neat solution 🤘!
Also not cheating at all!

Collapse
 
ogrotten profile image
ogrotten • Edited

Hmm... As a bootcamp student, I'm trying to untangle this.

[!(offsetIndex % 3) + 0]
I see this checks the modulus, and inverts the result. Any non-zero int is truthy, and this expression makes it false . . . +0 to coerce the false to an int. That is enough that the entire thing evaluates falsy, which then results in outputting offsetIndex on the otherside of the or. I had to drop this in a node repl to follow it, but I eventually got it 😁

But what is the ["", "Fizz"][!(offsetIndex % 3) + 0] double-array looking thing there? I thought it was a 2d array at first, but that doesn't seem right for a number of reasons.

Collapse
 
coreyja profile image
Corey Alexander

I'm pretty sure the first pair of square brackets creates an array, and the second one indexes into that array. So I think they are array indexing into the first array with either 0 or 1 to pick the empty string or "Fizz" depending on the offsetIndex!

Hope that helps!

Thread Thread
 
nombrekeff profile image
Keff

yup, it defines the array first const array = ["", "Fizz"] and then access the index array[!(offsetIndex % 3) + 0]. The expression will resolve either to true+0 -> 1 or false+0 -> 0

Thread Thread
 
ogrotten profile image
ogrotten

holy shit. that's cool.

I THOUGHT it might have been something like that, but I was thinking about it wrongly . . . I wasn't thinking of it as the array followed by the index, I was thinking of it as a variable. So ["an", "array"] was the name of the array, and then it had it's own index. Not very practical.

But the actual use is quite cool and makes plenty sense.

Thanks!

Collapse
 
speratus profile image
Andrew Luchuk

Thanks for the fun challenge! My solution uses bitwise operators and objects to get the answer:

function fizzbuzz(n) {
    for (let i = 1; i < n+1; i++) {
        outputs = {
            [i]: i,
            [i+1]: 'fizz',
            [i+2]: 'buzz',
            [i+3]: 'fizzbuzz'
        }
        let fizz = +(i % 3 == 0);
        let buzz = +(i % 5 == 0);
        buzz <<= 1;
        let fizzbuzz = fizz ^ buzz;
        console.log(outputs[i+fizzbuzz]);
    }
}
Collapse
 
nombrekeff profile image
Keff

Neat!

Collapse
 
russsaidwords profile image
Russ Edwards

Here's a solution to the challenge in JavaScript...

function fizzBuzz (n) {
    let i = 1
    let fbline = []
    let output = []
    let count = 0
    while (i <= n) {
        fbline = [i, i, i, "Fizz", i, "Buzz", "Fizz", i, i, "Fizz", "Buzz", i, "Fizz", i, i, "FizzBuzz"]
        output.push("" + fbline[(i+count) % 16])
        i++
        count = Math.floor(i / 16)
    }
    console.log(output)
}
fizzBuzz(90)

Here's a repl.it where you can run this.

Collapse
 
agtoever profile image
agtoever

A solution in Python. Feels silly though...

def fizzbuzz(n):
    offset = 0
    while n > 0:
        cycle = list(range(offset, 15 + offset))
        for i in range(3, 15, 3):
            cycle[i] = 'Fizz'
        for i in range(5, 15, 5):
            cycle[i] = 'Buzz'
        cycle.append('FizzBuzz')
        cycle.remove(offset)
        print('\n'.join(map(str, cycle[:min(15, n)])))
        offset += 15
        n -= 15


fizzbuzz(22)
Collapse
 
nombrekeff profile image
Keff

Wait until you see mine 😂

Collapse
 
agtoever profile image
agtoever • Edited

Holy sh*t, my other solution was really ugly! :-o
Here is a (much) better one (also in Python3):

def fizzbuzz(n):
    for i in range(1, n + 1):
        print([f'{i}', f'Fizz', f'Buzz', f'FizzBuzz'][(i % 3 == 0) + 2 * (i % 5 == 0)])

fizzbuzz(22)

This works using the property that True in Python has numerical value 1 and using f-strings in an array. The proper element in the array is chosen based on the mentioned property, checking for divisibility with 3 and 5.

Collapse
 
stephanie profile image
Stephanie Handsteiner • Edited

Easy, just do it in CSS.

ol {
    list-style-type: inside;
}

li:nth-child(3n), li:nth-child(5n) {
    list-style-type: none;
}

li:nth-child(3n):before {
    content: 'Fizz';
}

li:nth-child(5n):after {
    content: 'Buzz';
}
Enter fullscreen mode Exit fullscreen mode

Needs some Markup to display obviously.

Collapse
 
ben profile image
Ben Halpern

Ha!

Collapse
 
nombrekeff profile image
Keff

Magnificent! I knew there were going to be really neat solutions!!

Thanks for sharing your solution 💪

Collapse
 
rushsteve1 profile image
Steven vanZyl

The cleanest and best FizzBuzz implementation I know of doesn't use any if statements at all. Actually it doesn't use any control flow at all in most languages.
The approach is fully described here: philcrissman.net/posts/eulers-fizz...

On my Repl.it I also have this same approach implemented in several other languages:
repl.it/@rushsteve1/

Collapse
 
nombrekeff profile image
Keff

I did not know about this, thanks for sharing. I will remember this!

Collapse
 
coreyja profile image
Corey Alexander

This was great! Love it when there is a simple probable mathematic solution to these kinds of things!

Collapse
 
nombrekeff profile image
Keff

Me too, so clean! I love maths but I'm crap at it myself xD

Collapse
 
vonheikemen profile image
Heiker • Edited

You can still have flow control with functions.

const troo = (iff, elz) => iff;
const falz = (iff, elz) => elz;
const choose = (value) => [falz, troo][Number(Boolean(value))];

const is_fizz = (n) => choose(n % 3 === 0);
const is_buzz = (n) => choose(n % 5 === 0);

const fizzbuzz = (n) =>
  is_fizz(n) (
    () => is_buzz (n) ("FizzBuzz", "Fizz"),
    () => is_buzz (n) ("Buzz", n),
  )
    .call();

const range = (end) =>
  new Array(end).fill(null).map((val, index) => index + 1);

range(15).map(fizzbuzz).join(", ");
Collapse
 
nombrekeff profile image
Keff

I liked this approach! Thanks for sharing!

Collapse
 
jdaless profile image
John D'Alessandro

I do feel like this was kinda cheap...

using System;
using System.Collections.Generic;
using System.Linq;

public class Program
{
  public static void Main()
  {
    foreach(var s in FizzBuzz().Take(15))
      Console.WriteLine(s);
  }

  public static IEnumerable<string> FizzBuzz()
  {
    for(int i = 1; true; i++)
    {
      for(;i%3!=0 && i%5!=0;)
      {
        yield return i.ToString();
        break;
      }
      for(;i%3==0;)
      {
        for(;i%5==0;)
        {
          yield return "FizzBuzz";
          break;
        }
        yield return "Fizz";
        break;
      }
      for(;i%5==0;)
      {
        yield return "Buzz";
        break;
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
nocnica profile image
Nočnica Mellifera

I like it!

Collapse
 
nombrekeff profile image
Keff

Nice stuff! Cool use of a generator. And yeah kinda cheap but cool nonetheless, thanks for sharing!

Collapse
 
benwtrent profile image
Benjamin Trent • Edited

Clojure example.

I quite like Andrew's bit shift array example. Only think that its better to have a nil zeroth value so you get circuit breaking for free.

;; Array for grabbing appropriate string, if exists
(def fizzes [nil "Fizz" "Buzz" "FizzBuzz"])

;; boolean to integer conversion
(defn b-to-i [b]
  (bit-and 1 (bit-shift-right (Boolean/hashCode b) 1)))

(defn fizzit [n]
  (let [fizzed (b-to-i (= 0 (mod n 3)))                     ;1 if true
        buzzed (bit-shift-left (b-to-i (= 0 (mod n 5))) 1)  ;2 if true
        both (+ fizzed buzzed)]                             ;3 if both are true
    (or (get fizzes both) (str n)))
  )

(defn fizzbuzz [n]
  (map fizzit (range 1 (inc n))))

repl.it link

Collapse
 
nombrekeff profile image
Keff

Cool solution! I thought to do something similar at first, but ended up doing some weird stuff!

Collapse
 
mintypt profile image
mintyPT

Here is some python for you :)

print([
  (not (i % 3) and not (i % 5) and "FizzBuzz") or
  (not (i % 3) and "Fizz") or
  (not (i % 5) and "Buzz") or
  i
  for i in range(1,16)])
Collapse
 
rad_val_ profile image
Valentin Radu

Here's the simplest I can think of without any statements. 🙃

function run(n, i=1, j=1, k=1, acc=[]) {
  !j && k && acc.push('fizz')
  !k && j && acc.push('buzz')
  !k && !j && acc.push('fizzbuzz')
  k && j && acc.push(i)

    n - 1 && run(n - 1, i + 1, (j + 1) % 3, (k + 1) % 5, acc)
  return acc
}

console.log(run(30))
Collapse
 
nombrekeff profile image
Keff

Nice, recursion for the win 💪

Collapse
 
nombrekeff profile image
Keff

Thanks for sharing!

Collapse
 
nmampersand profile image
Nicole & (she/her)

fun challenge! this is the simplest thing I can think of at the moment

const fizzBuzz = (num) => {
    return [...Array(num+1).keys()].slice(1).map(n => {
        const fizzBuzz = n % 3 === 0 && n % 5 === 0 && 'FizzBuzz'
        const fizz = n % 3 === 0 && 'Fizz'
        const buzz = n % 5 === 0 && 'Buzz'
        return fizzBuzz || fizz || buzz || n.toString()
    })
}

console.log(fizzBuzz(15))