Table of contents
This article is my take on the "fizz buzz question". For those who are not familiar with fizz buzz, here is a quick reminder.
Fizz Buzz is a coding test / question, which, allegedly, is used or has been used during coding interviews. It goes as follow:
"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”."
The first time I heard about fizz buzz was watching a talk on YouTube. I don't remember exactly when and what was the title of the talk, however there was an example solution. Since then I have seen one or two solutions/implementations. An interesting one was in Clojure (and Haskell).
A lot of implementations check, I think, if a number is a multiple 3, 5 or 15. However during the talk I watched, the speaker mentioned that the number 15 is not in the problem's description; therefore, I think, 15 was not used in the example solution.
Here we are going to do the same, we are going to use the numbers 3 and 5 only; the number 15 will not be used.
Let's look at 2 possible solutions/implementations. They are not copied from anywhere, they are my own ideas. Even though someone else might have come up with something similar.
I am going to use the Java standard library only (but it is not a requirement); for no particular reason.
Implementation 1
The idea is to create a sort of pipeline, where the numbers flow and are transformed as needed.
Visually, it could look like this:
1 2 3 ... -> 1 2 fizz 4 buzz ... -> 1 2 ... 13 14 fizzbuzz 16 ...
We are going to use Map.Entry<K,V> to store the numbers and, possibly, their "fizz buzz representation". We could use Pair<K,V> if we were adding "apache commons lang3" to our project.
The work is made in the mapFizzBuzz
function/method which we simply call:
public void run() {
System.out.println(
String.join(" ", mapFizzBuzz())
);
}
mapFizzBuzz
streams all the numbers from 1 to 100. Here I am using Java 17 and can use the shorter syntax to aggregate the result.
The constants EMPTY, FIZZ and BUZZ will be reused in the second implementation.
public class Script {
private static final String EMPTY = "";
public static final String FIZZ = "fizz";
public static final String BUZZ = "buzz";
.
.
.
private List<String> mapFizzBuzz() {
return IntStream.rangeClosed(1, 100)
.mapToObj(i -> Map.entry(i, EMPTY))
.map(this::fizz)
.map(this::buzz)
.map(this::format)
.toList();
}
.
.
.
The method fizz
checks whether a number is a multiple of 3; the method buzz
if it is a multiple of 5.
private Entry<Integer, String> fizz(Entry<Integer, String> pair) {
if (pair.getKey() % 3 == 0) {
return Map.entry(pair.getKey(), pair.getValue() + FIZZ);
}
return pair;
}
private Entry<Integer, String> buzz(Entry<Integer, String> pair) {
if (pair.getKey() % 5 == 0) {
return Map.entry(pair.getKey(), pair.getValue() + BUZZ);
}
return pair;
}
The format
method formats what will be displayed on the screen; numbers with an empty "fizz buzz representation" are displayed unchanged.
private String format(Entry<Integer, String> pair) {
if (EMPTY.equals(pair.getValue())) {
return pair.getKey().toString();
}
return pair.getValue();
}
A class with a main
method is the entry point. Even though the project is simple, I have built it with Gradle. I'll add a link below to the code repository.
Here are the two main classes.
// FizzBuzzApplication.java
public class FizzBuzzApplication {
public static void main(String[] args) {
new Script().run();
}
}
---
// Script.java
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.stream.IntStream;
public class Script {
private static final String EMPTY = "";
public static final String FIZZ = "fizz";
public static final String BUZZ = "buzz";
public void run() {
System.out.println(
String.join(" ", mapFizzBuzz())
);
}
private List<String> mapFizzBuzz() {
return IntStream.rangeClosed(1, 100)
.mapToObj(i -> Map.entry(i, EMPTY))
.map(this::fizz)
.map(this::buzz)
.map(this::format)
.toList();
}
private Entry<Integer, String> fizz(Entry<Integer, String> pair) {
if (pair.getKey() % 3 == 0) {
return Map.entry(pair.getKey(), pair.getValue() + FIZZ);
}
return pair;
}
private Entry<Integer, String> buzz(Entry<Integer, String> pair) {
if (pair.getKey() % 5 == 0) {
return Map.entry(pair.getKey(), pair.getValue() + BUZZ);
}
return pair;
}
private String format(Entry<Integer, String> pair) {
if (EMPTY.equals(pair.getValue())) {
return pair.getKey().toString();
}
return pair.getValue();
}
}
Implementation 2
The second implementation is a bit more 'exotic', if I can use that term.
It was inspired by the fact that, during a coding task (a while ago :), someone found interesting that I used strings and the alphabet to solve the task, when they were (probably) expecting the use of a data structures. I might write, one day, about the above mentioned coding task.
So here, we are going to use strings, rather than Entry<K,V>
or Pair<K,V>
.
We are going to use a 'pipeline' again, that could look like this:
1 2 3 4 5 ... -> "1" "2" "3fizz" 4 "5buzz" ... -> "1" ... "14" "15fizzbuzz" ...
For clarity, I have done this implementation in the file ScriptString.java, with methods similar to those in the previous implementation.
import java.util.List;
import java.util.stream.IntStream;
public class ScriptString {
private static final String EMPTY = "";
public static final String FIZZ = "fizz";
public static final String BUZZ = "buzz";
public void run() {
System.out.println(
String.join(" ", stringFizzBuzz())
);
}
.
.
.
The method stringFizzBuzz
looks as follow:
private List<String> stringFizzBuzz() {
return IntStream.rangeClosed(1, 100)
.mapToObj(i -> i + EMPTY)
.map(this::stringFizz)
.map(this::stringBuzz)
.map(this::stringFormat)
.toList();
}
Numbers are first converted to strings: i + EMPTY
, before being processed further.
stringFizz
and stringBuzz
check if numbers are multiples of 3 and 5 respectively.
stringFormat
formats appropriately.
Those methods have to 'extract' a number from a string before checking it. They use a utility function: removeFizzBuzzText
and then try to convert a string to a number. If there is an exception during the conversion, it is caught.
stringFormat
does some work before results are displayed: "1" stays "1"; "3fizz" becomes "fizz".
The full Java class is as follow.
import java.util.List;
import java.util.stream.IntStream;
public class ScriptString {
private static final String EMPTY = "";
public static final String FIZZ = "fizz";
public static final String BUZZ = "buzz";
public void run() {
System.out.println(
String.join(" ", stringFizzBuzz())
);
}
private List<String> stringFizzBuzz() {
return IntStream.rangeClosed(1, 100)
.mapToObj(i -> i + EMPTY)
.map(this::stringFizz)
.map(this::stringBuzz)
.map(this::stringFormat)
.toList();
}
private String stringFizz(String string) {
try {
int i = Integer.parseInt(removeFizzBuzzText(string));
if (i % 3 == 0) {
return (string + FIZZ);
}
} catch (NumberFormatException e) {}
return string;
}
private String stringBuzz(String string) {
try {
int i = Integer.parseInt(removeFizzBuzzText(string));
if (i % 5 == 0) {
return (string + BUZZ);
}
} catch (NumberFormatException e) {}
return string;
}
private String stringFormat(String string) {
try {
Integer.parseInt(string);
return string;
}
catch (NumberFormatException e) {
if (string.contains(FIZZ)) {
return string.substring(string.indexOf(FIZZ));
}
return string.substring(string.indexOf(BUZZ));
}
}
private String removeFizzBuzzText(String string) {
return string.replace(FIZZ, EMPTY).replace(BUZZ, EMPTY);
}
}
The result
Summary
In this article we explored 2 solutions to the "fizz buzz" coding task. We checked if numbers were multiple of 3 and 5; we did not use the number 15. The second solution is a bit unusual as it uses strings to implement the solution.