Skip to content

Instantly share code, notes, and snippets.

@beneggett
Last active January 23, 2019 12:16
Show Gist options
  • Save beneggett/9108822 to your computer and use it in GitHub Desktop.
Save beneggett/9108822 to your computer and use it in GitHub Desktop.
Fizz Buzz Test

Fizz Buzz Test

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”.

Answer & Refinement process

Don't look until you want to see how I thought through it:

Final Shortest Answer

puts (1..100).collect { |i| (fb = [["Fizz"][i % 3], ["Buzz"][i % 5]].compact.join).empty? ? i : fb }

or

puts (1..100).map { |i| (fb = [["Fizz"][i % 3], ["Buzz"][i % 5]].compact.join).empty? ? i : fb }

Use map if you want to save 4 characters :)

How I arrived there:

We need to take a number and see if it is divisible by 3, 5 respectively.

In programming, we have a % operator called Modulo, it will divide a number and return the remainder.

e.g.

3 % 3 
=> 0
4 % 3 
=> 1
5 % 3 
=> 2
6 % 3 
=> 0

Simplest working solution

Therefore, the most basic adaptation to check if a number is divisible would be something like:

# Loops through each number, 1-100
(1..100).each do |i|

  if i % 3 == 0 and i % 5 == 0
    # Checks if integer is divisible by both 3 & 5
    puts "FizzBuzz"
  elsif i % 3 == 0
    # Checks if integer is divisible by only 3
    puts "Fizz"
  elsif i % 5 == 0
    # Checks if integer is divisible by only 5
    puts "Buzz"
  else
    # Otherwise prints the number itself
    puts i
  end
end

When run, we get a result like:

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
31
32
Fizz
34
Buzz
Fizz
37
38
Fizz
Buzz
41
Fizz
43
44
FizzBuzz
46
47
Fizz
49
Buzz
Fizz
52
53
Fizz
Buzz
56
Fizz
58
59
FizzBuzz
61
62
Fizz
64
Buzz
Fizz
67
68
Fizz
Buzz
71
Fizz
73
74
FizzBuzz
76
77
Fizz
79
Buzz
Fizz
82
83
Fizz
Buzz
86
Fizz
88
89
FizzBuzz
91
92
Fizz
94
Buzz
Fizz
97
98
Fizz
Buzz
=> 1..100

That seems to be the simplest working version, but there is a lot of similar logic, multiple calls to puts, and the return value is 1..100. We can refactor.

Refactoring Part One

As mentioned there is a repeating pattern in our logic in what determines the appropriate output. When Using the modulo operator, we are looking for clean division, meaning no remainder, meaning that value must always equal 0 to be true.

Restated: The number qualifies for a 'special output' when the result == 0

0 is a special number in programming that can be used in unique ways. One way is, we could use 0 to return the first element in an array, such as:

["Fizz"][0]
=> ["Fizz"]

Which stands to reason we could do something like:

["Fizz"][i % 3]

and

["Buzz"][i % 5]

We have a lot of capabilities when it comes to manipulating data in an array. We can nest arrays, join arrays, and so much more. Let's combine our two arrays:

# let's assume i = 3
i = 3
[["Fizz"][i % 3], ["Buzz"][i % 5]]
=> ["Fizz", nil]
# Note, the index 2 of buzz is nil, there is no third element in the array

# let's assume i = 5
i = 5
[["Fizz"][i % 3], ["Buzz"][i % 5]]
=> [ nil, "Buzz"]
# Similarly, as 5 is greater than 3, the remainder 3 returns, the 3 index of ["Buzz"] is also nil

# let's assume i = 15
i = 5
[["Fizz"][i % 3], ["Buzz"][i % 5]]
=> [ "Fizz", "Buzz"]
# now this is interesting, and some data we can work with

# and lastly, let's assume a 'non-qualifier number', i = 2
i = 2
[["Fizz"][i % 3], ["Buzz"][i % 5]]
=> [ nil, nil]

Ok, now we have two values in an array, each with a possibility of being nil, "fizz" or "buzz"

[nil, nil]        # Non qualifiers
["Fizz", nil]     # Numbers only divisible by 3
[nil, "Buzz"]     # Numbers only divisible by 5
["Fizz", "Buzz"]  # Numbers divisible by both 3 & 5

So we have these pesky nil numbers we would like to get rid of, as they contribute nothing, and in the case of nil, we want to simply return the number.

The Ruby Array Class gives us a compact method that will remove any nil values from an array, thus if we run .compact on the end of them, it will clean right up:

[nil, nil].compact
=> []      
["Fizz", nil].compact
=> ["Fizz"]   
[nil, "Buzz"].compact
=> ["Buzz"]   
["Fizz", "Buzz"].compact
=> ["Fizz", "Buzz"]

Now that we've cleaned those up, we can join our strings in the array together using our join method:

[nil, nil].compact.join
=> ""      
["Fizz", nil].compact.join
=> "Fizz"
[nil, "Buzz"].compact.join
=> "Buzz"
["Fizz", "Buzz"].compact.join
=> "FizzBuzz"

Ok, These outputs look like something we like. We'll still need to solve for the "" empty string when i is a non-qualifying number, but that's now only one conditional statement.

Let's string all this back together:

[["Fizz"][i % 3], ["Buzz"][i % 5]].compact.join

And now apply this back to our original solution:

(1..100).each do |i|
  puts [["Fizz"][i % 3], ["Buzz"][i % 5]].compact.join
end

Result:




Fizz

Buzz
Fizz


Fizz
Buzz

Fizz


FizzBuzz


Fizz

Buzz
Fizz


Fizz
Buzz

Fizz


FizzBuzz


Fizz

Buzz
Fizz


Fizz
Buzz

Fizz


FizzBuzz


Fizz

Buzz
Fizz


Fizz
Buzz

Fizz


FizzBuzz


Fizz

Buzz
Fizz


Fizz
Buzz

Fizz


FizzBuzz


Fizz

Buzz
Fizz


Fizz
Buzz

Fizz


FizzBuzz


Fizz

Buzz
Fizz


Fizz
Buzz
=> 1..100

Refinement Part 2

So we see the logic part of it is working, now we just need to get our integers back when it is an empty string:

(1..100).each do |i|
  if [["Fizz"][i % 3], ["Buzz"][i % 5]].compact.join == ""
    puts i
  else
    puts [["Fizz"][i % 3], ["Buzz"][i % 5]].compact.join
  end
end

Even this can be refactored a few steps further. == "" is not a great conditional statement, we have multiple ruby methods that check empty strings for us: empty?, blank?, present? (but the opposite). I like empty because not only will it catch "", but also nil & empty arrays.

(1..100).each do |i|
  if [["Fizz"][i % 3], ["Buzz"][i % 5]].compact.join.empty?
    puts i
  else
    puts [["Fizz"][i % 3], ["Buzz"][i % 5]].compact.join
  end
end

We still have that big long [["Fizz"][i % 3], ["Buzz"][i % 5]].compact.join that is in there twice. Seems repetitive. Frankly, this is a simple if else statement, and a ternary operator could shrink the logic part of it down to a cleaner, simpler, one line solution for us:

(1..100).each do |i|
  puts (fb = [["Fizz"][i % 3], ["Buzz"][i % 5]].compact.join).empty? ? i : fb
end

Note, I set a variable fb here, so I can reference that long equation in my else clause, without retyping

Result:

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
31
32
Fizz
34
Buzz
Fizz
37
38
Fizz
Buzz
41
Fizz
43
44
FizzBuzz
46
47
Fizz
49
Buzz
Fizz
52
53
Fizz
Buzz
56
Fizz
58
59
FizzBuzz
61
62
Fizz
64
Buzz
Fizz
67
68
Fizz
Buzz
71
Fizz
73
74
FizzBuzz
76
77
Fizz
79
Buzz
Fizz
82
83
Fizz
Buzz
86
Fizz
88
89
FizzBuzz
91
92
Fizz
94
Buzz
Fizz
97
98
Fizz
Buzz
=> 1..100

Refinement part 3

Ok it's looking pretty good. I'm satisfied with the printing, but I still don't like how it's returning the 1..100 range as a result. Instead of each, let's use map or collect (synonyms). Remember, collect/map will pull it all into a single array/output.

(1..100).collect do |i|
  (fb = [["Fizz"][i % 3], ["Buzz"][i % 5]].compact.join).empty? ? i : fb
end

# Note I'm not calling puts in this example...yet.

The nice thing about the map/collect method is there is a shorthand way of writing blocks as well, rather than .collect do |i| ... end, we can use .collect { |i| .... } syntax.

(1..100).collect { |i| (fb = [["Fizz"][i % 3], ["Buzz"][i % 5]].compact.join).empty? ? i : fb }

And re-adding puts to the beginning of the line accomplishes our print instruction

Final Condensed Answer:

puts (1..100).collect { |i| (fb = [["Fizz"][i % 3], ["Buzz"][i % 5]].compact.join).empty? ? i : fb }

Outputs:

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
31
32
Fizz
34
Buzz
Fizz
37
38
Fizz
Buzz
41
Fizz
43
44
FizzBuzz
46
47
Fizz
49
Buzz
Fizz
52
53
Fizz
Buzz
56
Fizz
58
59
FizzBuzz
61
62
Fizz
64
Buzz
Fizz
67
68
Fizz
Buzz
71
Fizz
73
74
FizzBuzz
76
77
Fizz
79
Buzz
Fizz
82
83
Fizz
Buzz
86
Fizz
88
89
FizzBuzz
91
92
Fizz
94
Buzz
Fizz
97
98
Fizz
Buzz
=> nil

And returns nil, just like we want.

@vidkun
Copy link

vidkun commented Aug 25, 2014

I got mine down to 85 characters:

1.upto(100) {|n| p (n%3==0 ? (n%5==0 ? "FizzBuzz" : "Fizz") : (n%5==0 ? "Buzz" : n))}

@paveljurca
Copy link

perl -E'say+(join"",map{$_->{0}}({$_%3,"Fizz"},{$_%5,"Buzz"}))||$_ for 1..100'

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment