FizzBuzz is a legendary coding challenge. You simply must give it a try! But… what approaches can you use to solve FizzBuzz with the Swift programming language?
In this tutorial, we’re going to solve the FizzBuzz challenge in three different ways:
- With conditionals
- With the switch statement
- With .map()
Table of Contents
What is FizzBuzz?
FizzBuzz is a coding challenge. Its instructions are:
- Write a program that prints the numbers from 1 to 100
- For numbers divisible by 3, print “Fizz”
- For numbers divisible by 5, print “Buzz”
- For numbers divisible by both 3 and 5, print “FizzBuzz”
The point of FizzBuzz is to “test” if a person knows how to code. It’s commonly used in coding interviews to test the problem-solving and coding skills of a candidate.
What’s so interesting about FizzBuzz is that its solution is counter-intuitive, and can’t be solved elegantly or efficiently. This often confuses beginner programmers, which makes it an interesting challenge.
As you’ll see in the next section, FizzBuzz can’t be solved with a simple if-this-then-that approach. You’ll have to duplicate some of your code, which will make you second-guess your solution if you wanted to make your code as elegant as possible.
Let’s have a look then, shall we?
Fun Fact: No employer or client has ever asked me to solve FizzBuzz in a coding interview. They did ask me, however, to sort giraffes in groups of ascending heights. The differences in heights in a group couldn’t exceed 1, because that would make the giraffes insecure… I’ve included a few brain-tickling coding challenges at the end of this tutorial.
Solving FizzBuzz with Conditionals
We’re now going to write the most basic solution for FizzBuzz, step-by-step. The first step is of course, a for loop:
for i in 1...100
{
print(i)
}
The above for loop will loop over the range 1...100 and print out its individual numbers. So far so good!
The next step is to find out if a number is divisible by 3. Here’s how:
for i in 1...100
{
if i % 3 == 0 {
print("Fizz")
} else {
print(i)
}
}
That results in the output:
1, 2, Fizz, 4, 5, Fizz, 7, 8, Fizz, 10, 11, Fizz
As expected, the numbers 3, 6, 9, 12, etc. are replaced by “Fizz”. Any other number is simply printed out as-is. So, how does that work?
The expression i % 3 == 0 uses the modulo operator % to find the remainder of a division. It divides i by 3 and sees if there’s something “left”. For instance, 5 divided by 3 has 2 remaining, i.e. 5 % 3 = 2. When the remainder is 0 you know for certain that i is divisible by 3.
The result of the i % 3 == 0 expression is a boolean, so you can use it in an if-statement (also called a conditional). When the expression is true, “Fizz” is printed. When it’s false, the else block is executed and the number i is printed.
Instead of the modulo operator %, you can also use i.isMultiple(of: 3) in Swift. This is clearer but more verbose.
Now, let’s extend the code to also test for divisibility by 5. Like this:
for i in 1...100
{
if i % 3 == 0 {
print("Fizz")
} else if i % 5 == 0 {
print("Buzz")
} else {
print(i)
}
}
Its output is:
1, 2, Fizz, 4, Buzz, Fizz, 7, 8, Fizz, Buzz, 11
As expected, the numbers 5 and 10 are replaced with “Buzz”. The else if block checks that i % 5 == 0 is true, and prints out “Buzz”.
Why doesn’t it print “FizzBuzz” too at this point? Because the if, else if and else blocks are evaluated one by one, from top to bottom. When one expression is true, the expressions below it aren’t executed. So, when i is divisible by 3, the first block executes, and the other block do not.
So… how can you incorporate “FizzBuzz” at this point? That’s what the whole challenge is about! You have two options:
- You add another if i % 5 == 0 conditional inside the first i % 3 conditional. After all, you’re checking that i is divisible by both 3 and 5.
- You add a top-level if i % 3 == 0 && i % 5 == 0 to the code. This way you’re evaluating the value i twice.
Let’s look at the first approach. It goes like this:
for i in 1...100
{
if i % 3 == 0 {
if i % 5 == 0 {
print("FizzBuzz")
} else {
print("Fizz")
}
} else if i % 5 == 0 {
print("Buzz")
} else {
print(i)
}
}
Do you see that nested if-statement inside the first i % 3 == 0 block? It works like this:
- Check if i is divisible by 3, and if that’s true:
- Check if i is divisible by 5, and if that’s true, print “FizzBuzz”
- If that’s not true, in else, print “Fizz”
- (The other conditionals still work the same.)
This code is pretty hard to read. You can’t immediately assess the behavior of every conditional, and you can’t shake the inelegance of executing i % 5 twice.
Can we use a different approach? Yes! Have a look:
for i in 1...100
{
if i % 3 == 0 && i % 5 == 0 {
print("FizzBuzz")
} else if i % 3 == 0 {
print("Fizz")
} else if i % 5 == 0 {
print("Buzz")
} else {
print(i)
}
}
Much better, right? We’re still executing some code twice, but at least it’s readable. You can see at a glance that we’re checking if i is divisible by both 3 and 5, or just by one of them, or else.
However, this solution still executes both i % 3 == 0 and i % 5 == 0 twice, at worst. The code first evaluates i % 3 == 0 && i % 5 == 0, and then those divisions individually.
A most efficient value of i here is 15, for example, because that hits the first conditional immediately. A most inefficient value is one you can’t divide by 3 or 5, like 7, so you have to execute every conditional before ending up at else.
Solving FizzBuzz with Switch and Pattern Matching
The Swift programming language has a very cool feature called pattern matching. We can use it with the switch statement to match its different values with different switch cases.
Let’s have a look:
for i in 1...100
{
switch (i % 3 == 0, i % 5 == 0)
{
case (true, false):
print("Fizz")
case (false, true):
print("Buzz")
case (true, true):
print("FizzBuzz")
default:
print(i)
}
}
The code is similar to what we had before. We’re looping over 1...100 and printing “Fizz”, “Buzz”, “FizzBuzz” or a number for every iteration of the loop.
Instead of an if statement, we’re using switch. In short, a switch statement takes one expression, and has a number of case blocks for every possible value of that expression.
In the above code, we’re constructing a tuple out of the expression (i % 3 == 0, i % 5 == 0). A tuple combines two values in an ordered list. Because both values in the tuple can be true or false, the full tuple now has 4 different values:
(true, true)
(false, false)
(true, false)
(false, true)
In the switch statement we can now match those tuples to different case blocks. Here, walk through this list on your own:
- (true, false) means that i is only divisible by 3
- (false, true) means that i is only divisible by 5
- (true, true) means that i is divisible by both 3 and 5
- (false, false) means that i isn’t divisible by neither 3 nor 5
In code, that looks like this:
switch (i % 3 == 0, i % 5 == 0)
{
case (true, false):
print("Fizz")
case (false, true):
print("Buzz")
case (true, true):
print("FizzBuzz")
default:
print(i)
}
See how the expressions i % 3 == 0 and i % 5 == 0 are assessed, and its values matched to different case blocks? Note that the default block matches anything that the other blocks don’t match.
You know what’s so cool about the above code? The (i % 3 == 0, i % 5 == 0) expression is evaluated once for every iteration of i. Unlike the previous example, with the if statements, the code that evaluates if i is divisible only runs once. Albeit for every iteration of i, but that’s still better than twice for every iteration of i.
Note: The switch statement in Swift has no implicit fall-through, unlike other programming languages. You won’t have to explicitly break a case. When you want a case to fall-through, use fallthrough explicitly.
Just for fun, let’s look at another way of solving FizzBuzz that’s identical to the one with switch, but syntactically different. Check this out:
let FIZZ = true
let BUZZ = true
for i in 1...100
{
switch (i % 3 == 0, i % 5 == 0)
{
case (FIZZ, BUZZ):
print("FizzBuzz")
case (FIZZ, _):
print("Fizz")
case (_, BUZZ):
print("Buzz")
default:
print(i)
}
}
What’s going on here? We’ve changed a few things:
- At the top of the code we’ve defined constants FIZZ and BUZZ, both set to true. They’re going to serve as “aliases” for the Fizz, Buzz and FizzBuzz numbers in the range from 1 to 100.
- Instead of pattern matching with true and false, we’re now using the constants (“aliases”) FIZZ and BUZZ for true in case. We’ve changed false in an underscore _, so we’re ignoring those false values.
- The order of cases in switch has changed. We’ll need to make sure that the pattern for FizzBuzz matches first. If we hadn’t, the “Fizz” case would also match for “FizzBuzz” because we’re ignoring the false value matches.
Even though the way the code works is still the same, it’s syntactically different. We’re using the FIZZ and BUZZ constants to obscure the pattern matching in case, which makes the code more expressive. Neat!
Big thanks for this approach to Tim “Mr. C” Colson.
Solving FizzBuzz with “map(_:)”
The previous examples all printed out values to the Console, but what if you want to evaluate a range of FizzBuzz as a whole?
Let’s spice up FizzBuzz! Just because we can. It’s about playing with code, right?
First, we’re going to put FizzBuzz into a closure. Like this:
let fizzbuzz:(Int) -> String = { i in
switch (i % 3 == 0, i % 5 == 0)
{
case (true, false):
return "Fizz"
case (false, true):
return "Buzz"
case (true, true):
return "FizzBuzz"
default:
return "\(i)"
}
}
The code is similar to what you’ve seen before. Instead right now, we can call fizzbuzz() for any arbitrary value and get the right string back.
fizzbuzz(15)
// Output: FizzBuzz
fizzbuzz(3)
// Output: Fizz
And then here’s a beautiful one-liner:
Array(1...100).map(fizzbuzz).joined(separator: ", ")
Whaaat? How does it work? It’s simple:
- Create an array with integer values from 1 to 100
- Call map(_:) on that array, calling the closure fizzbuzz for every item in the array, mapping integer values to string values
- Calling joined(separator:) on the resulting array of strings, effectively glueing them together with commas
The result:
1, 2, Fizz, 4, Buzz, Fizz, 7, 8, Fizz, Buzz, 11, Fizz, 13, 14, FizzBuzz, 16, 17, … FizzBuzz, 91, 92, Fizz, 94, Buzz, Fizz, 97, 98, Fizz, Buzz
Awesome!
Further Reading
FizzBuzz is a terrific primer for coding interview challenges. It helps you to think strategically instead of intuitively, and to assess every different scenario and edge case that can occur in your code. And it’s a whole lot of fun!
Related Articles
- How To Detect Internet Connectivity with NWPathMonitor
- Random Numbers in Swift
- How To: Pass Data Between View Controllers in Swift
- Get Started with Xcode Playgrounds
- Scene Delegate vs. App Delegate Explained
- Networking in Swift with URLSession
- 5 Quick Tips for the Xcode 11 Minimap
- For Loops in Swift (How To)
- How To: Get Started With CocoaPods
- Dependency Injection in Swift
- How To Download, Install and Update Xcode
- Escaping Closures in Swift Explained
- How To Generate a Random Unique Identifier with UUID in Swift
- Working with Codable and JSON in Swift
- Play With Code: Palindromes In Swift