There’s a simple child’s game called Fizz Buzz that is also used as a test for developers to show basic coding skills. Jeff Atwood even mentioned in his blog the use of this game as a simple test to weed out the bad developers before hiring new ones. And there are still developers being hired these days that would fail the simple FizzBuzz test. And that’s a shame.
So, what is Fizz Buzz? Well, basically you will count from 1 to 100 but when you encounter a multiple of three, you say “Fizz” instead of the number. And if it’s a multiple of five, you’ll say “Buzz” instead. So if you encounter a multiple of three and five, you’ll have to say “Fizz Buzz”. It’s a simple math challenge for children and a coding challenge for developers. A good developer should be able to write this code within 10 minutes or so, which makes it an excellent test during the hiring process…
Now, the website “Rosetta Code” has an excellent solution of solving this problem, and many others, in a lot of different programming languages. These include several solutions for C#. (With C# being my most popular choice these days.) But I want to show how to FizzBuzz in a complex way with lots of data analysis and solve it through method chaining. In my previous post I already mentioned why using methods that return void will break a method chain. This post will show some interesting code aspects of how to split even a simple problem into many smaller parts so each piece can be solved and managed but also reused in other projects.
So, let’s analyse the data we need and the data we will generate. Basically, we start with an array of numbers that gets translated to a single string. So, here we go:
- We need to make a list of numbers.
- Create pairs so we can store each call.
- We need to return “Fizz”.
- We need to return “Buzz”.
- We need to return numbers as string values.
- We need a condition telling us when to Fizz.
- We need a condition telling us when to Buzz.
- We need a condition telling us when we to use a number.
- We need to select just the string values.
- We have to join the string values into a comma-separated string.
So, I generally start with creating a static class as I’m going to make a bunch of extension methods for C#. Why? Because extension methods allow me to add functionality to objects and classes without modifying them! But first let’s make a few friendlier names for data types that we will be “using”… (Pun intended!)
using IntEnum = IEnumerable<int>;
using Pair = KeyValuePair<int, string>;
using PairEnum = IEnumerable>< KeyValuePair<int, string>>;
using StringEnum = IEnumerable<string>;
That’s the data we will need. We will need an enumeration of integers. Those will be converted to pairs so we can maintain the proper order and know which number belongs to which result. These pairs are also enumerated and in the end, they will be converted to strings before we make a single string out of them all…
So now we’re going through the list of functions:
Make a list of numbers.
This is an easy one. But while many might just create a for-loop to walk through, I prefer to create an enumeration instead, as this allows me to “flow” all the data. So my first method:
public static IntEnum MakeArray(this int count, int start = 0) => Enumerable.Range(start, count);
This is a simple trich that not many developers are familiar with. Instead of using a for-loop, I use Enumerable.Range() to create an enumeration to start my method chain. I can now use 50.MakeArray(10)
to make an enumeration that starts at 10 and goes up to 50.
Create pairs so we can store each call.
Here we will create a few methods. First, we’ll start with:
public static Pair NewPair(this int key) => new Pair(key,string.Empty);
This code takes an integer and will return a key pair with empty string as value. Then we’ll create:
public static Pair NewPair(this Pair pair, string data) => new Pair(pair.Key, data.Trim());
Here we create new key pair based on the old key pair. Thus we won’t change existing key pairs as those might still be used for other purposes. We also trim the string value to remove excess spaces.
We need to return “Fizz”.
Seriously? Well, yeah.
public static Pair DoFizz(this Pair data) => data.NewPair("Fizz " + data.Value);
The reasoning behind it is that we want to add the word “Fizz” in front of the string that we already have. As we start with empty strings and trim the value when creating a new pair, we should just get “Fizz” as result. But if the result already contains “Buzz”, we will get “Fizz Buzz” as result. Including the space!
We need to return “Buzz”.
This is oh, so simple. 🙂
public static Pair DoBuzz(this Pair data) => data.NewPair(data.Value + " Buzz");
The difference between this method and the DoFizz() method is that this one adds “Buzz” at the end of the result, not at the start. So it doesn’t matter if we first Fizz or Buzz, the result is always “Fizz Buzz”. Including the space…
We need to return numbers as string values.
More simple code, but even simpler…
public static Pair DoNumber(this Pair data) => data.NewPair(data.Key.ToString());
When we put a number in the result, we don’t care about it’s content. It gets replaced in all cases…
We need a condition telling us when to Fizz.
We can’t Fizz all the time so we need to have a condition check.
public static bool IsFizz(this Pair data) => data.Key % 3 == 0;
I could have made it even more generic with an int as parameter but I use this solution to make it readable!
We need a condition telling us when to Buzz.
We also need to determine when to Buzz…
public static bool IsBuzz(this Pair data) => data.Key % 5 == 0;
We need a condition telling us when we to use a number.
And we need to decide when we want to have a number as result!
public static bool IsNumber(this Pair data) => string.IsNullOrEmpty(data.Value);
But how to get those conditional statements inside my enumerations?
Well, we need the if-statement to become part of the method chain. So we create another extension method for it.
public static T If(this T data, Func condition, Func action) => condition(data) ? action(data) : data;
This shows some power behind extension methods, doesn’t it? Because I’ve declared both the condition and action as separate methods, I can use a shorthand method when calling them. And the shorthand I will use for the Fizz(), Buzz() and Number() methods I’m creating:
public static Pair Fizz(this Pair data) => data.If(IsFizz, DoFizz);
public static Pair Buzz(this Pair data) => data.If(IsBuzz, DoBuzz);
public static Pair Number(this Pair data) => data.If(IsNumber, DoNumber);
And as these methods work on just a single pair, I also need methods to use for the full enumeration. Fortunately, I can use the same name, with different types:
public static PairEnum Fizz(this PairEnum data) => data.Select(Fizz);
public static PairEnum Buzz(this PairEnum data) => data.Select(Buzz);
public static PairEnum Number(this PairEnum data) => data.Select(Number);
The PairEnum
is just an IEnumerate<Pair>
as declared by the “using” before. Rather than declaring a new class, I prefer to create an alias for this type.
The result is that we have three methods now which each will work on an enumeration, changing the result of each pair where this is required.
We need to select just the string values.
This is another simple one, but here the data flow changes type. Selecting the results can be done by using:
public static StringEnum Values(this PairEnum data) => data.Select(p => p.Value);
But before selecting all results, we might want to put all items in the correct order:
public static IOrderedEnumerable SortPairs(this IEnumerable data) => data.OrderBy(p => p.Key);
Now, this sort is not required when you go through the enumerations normally. But because you can also do parallel enumerating and thus use multiple threads to walk through everything, you might also get all results in a random order. So, sorting it will fix this…
How do we make it execute parallel? Well, I would just need one adjustment to one method:
public static PairEnum Number(this PairEnum data) => data.AsParallel().Select(Number);
I only added AsParallel() in this method to change it from a synchronous to an asynchronous enumeration. But the result is that all numbers will be executed asynchronously. This means the order of my data becomes unpredictable. And let’s make it even better:
public static PairEnum Number(this PairEnum data, bool asynchronous = false) => asynchronous ? data.AsParallel().Select(Number) : data.Select(Number);
Now we can specify if we want to execute the numbering synchronous or not. The default will be synchronous so I would not need to sort afterwards. But if I indicate that I want it executed asynchronous then the order of data will be more randomized and I would need to sort afterwards.
If I also apply this change to Fizz() and Buzz() then it can become extremely interesting when everything is done asynchronous.
We have to join the string values into a comma-separated string.
Well, joining string values in a list is simply done by calling the string.Join() method but as I’m making a lot of methods already, I can just add one more:
public static string Combine(this StringEnum data) => string.Join(", ", data);
And all these methods together allow me to control the data flow in the finest details. Generally too fine for solving the FizzBuzz challenge, but it does make an interesting example…
So, how to FizzBuzz?
Okay, one more method:
public static string FizzBuzz(this int count) =>
count.MakeArray(1)
.MakePairs()
.Fizz()
.Buzz()
.Number()
.Values()
.Combine();
Notice how all methods used so far are basically one-liners. And this method too is just a single statement. It’s just written over multiple lines to make it readable.
And it shows the data flow in reasonably clear names. We take a number and make an array of numbers. These get converted to pairs, the pairs then get Fizzed, Buzzed and numbered before we convert them to strings and join them into a single string result. No sorting as I’m not doing anything asynchronous here. And the result will be: Buzz, 1, 2, Fizz, 4, Buzz, Fizz, 7, 8, Fizz, Buzz, 11, Fizz, 13, 14, Fizz Buzz, 16, 17, Fizz, 19, Buzz, Fizz, 22, 23, Fizz, Buzz, 26, Fizz, 28, 29, Fizz Buzz, 31, 32, Fizz, 34, Buzz, Fizz, 37, 38, Fizz, Buzz, 41, Fizz, 43, 44, Fizz Buzz, 46, 47, Fizz, 49, Buzz, Fizz, 52, 53, Fizz, Buzz, 56, Fizz, 58, 59, Fizz Buzz, 61, 62, Fizz, 64, Buzz, Fizz, 67, 68, Fizz, Buzz, 71, Fizz, 73, 74, Fizz Buzz, 76, 77, Fizz, 79, Buzz, Fizz, 82, 83, Fizz, Buzz, 86, Fizz, 88, 89, Fizz Buzz, 91, 92, Fizz, 94, Buzz, Fizz, 97, 98, Fizz, 100
Which reminds me: I had to use MakeArray(1)
to make sure I start with 1, not 0. Otherwise, I would get 100 numbers starting with 0 and ending at 99, not 100…
Now, you’re probably thinking about how this works. And it’s likely that you assume it creates a list of integers, then a list of pairs, then a list of Fizzed pairs, etcetera. And you’re wrong! But let’s modify the code a bit. Let’s display technical information using my Do() method, and a variant of Do()…
Adding Do…
public static T Do<T>(this T data, Action<T> action) { action(data); return data; }
public static T Do<T>(this T data, Action action) { action(); return data; }
public static IEnumerable<T> Do<T>(this IEnumerable<T> data, Action<T> action) { foreach (var item in data) { action(item); yield return item; } }
These three Do() methods are very generic and as I’ve mentioned in my previous post, it allows using methods returning void within a method chain.
The first Do() will perform an action and pass the data back to that method. As we just want to do simple WriteLine statements, we won’t use that.
The second Do() can perform actions that don’t depend on the data. This is ideal to “step out” of the method chain to do something else before returning to the chain.
The third Do() will do an action for each item inside an enumeration without breaking the enumeration! And why we want to do this will be shown when we call this monster:
public static string FizzBuzzNormalStatus(this int count) =>
count.Do(() => Console.WriteLine("Linear: Initializing…"))
.MakeArray()
.Do(() => Console.WriteLine("Linear: Array created…"))
.MakePairs()
.Do(pair => Console.WriteLine($"Pair: {pair.Key} -> {pair.Value}"))
.Do(() => Console.WriteLine("Linear: Pairs made…"))
.Fizz()
.Do(pair => Console.WriteLine($"Fizz: {pair.Key} -> {pair.Value}"))
.Do(() => Console.WriteLine("Linear: Fizzed…"))
.Buzz()
.Do(pair => Console.WriteLine($"Buzz: {pair.Key} -> {pair.Value}"))
.Do(() => Console.WriteLine("Linear: Buzzed…"))
.Number()
.Do(pair => Console.WriteLine($"Number: {pair.Key} -> {pair.Value}"))
.Do(() => Console.WriteLine("Linear: Numbered…"))
.SortPairs()
.Do(pair => Console.WriteLine($"Sort: {pair.Key} -> {pair.Value}"))
.Do(() => Console.WriteLine("Linear: Sorted…"))
.Values()
.Do(() => Console.WriteLine("Linear: Extracted…"))
.Combine()
.Do((s) => Console.WriteLine($"Linear: Finalizing: {s}…"));
And yes, this is still a single statement. It’s huge because I’m writing a line after each method indicating that this method has just fired after writing all the data it has processed. So, you would expect to see “Linear: Array created…” being written, followed by a bunch of pairs and then “Linear: Pairs made…”.
Wrong!
But I’ll show this by FizzBuzzing just the numbers 0 to 4:
Linear: Initializing…
Linear: Array created…
Linear: Pairs made…
Linear: Fizzed…
Linear: Buzzed…
Linear: Numbered…
Linear: Sorted…
Linear: Extracted…
Pair: 0 ->
Fizz: 0 -> Fizz
Buzz: 0 -> Fizz Buzz
Number: 0 -> Fizz Buzz
Pair: 1 ->
Fizz: 1 ->
Buzz: 1 ->
Number: 1 -> 1
Pair: 2 ->
Fizz: 2 ->
Buzz: 2 ->
Number: 2 -> 2
Pair: 3 ->
Fizz: 3 -> Fizz
Buzz: 3 -> Fizz
Number: 3 -> Fizz
Pair: 4 ->
Fizz: 4 ->
Buzz: 4 ->
Number: 4 -> 4
Sort: 0 -> Fizz Buzz
Sort: 1 -> 1
Sort: 2 -> 2
Sort: 3 -> Fizz
Sort: 4 -> 4
Linear: Finalizing: Fizz Buzz, 1, 2, Fizz, 4…
And yes, that’s the output of this code! It tells you the array was created, the pairs prepared, Fizzed, Buzzed, Numbered before sorting and extracting and only then will it do all those things!
This is what makes enumerations so challenging to understand! They’re not arrays or lists. They’re something many developers are less familiar with. This is why “yield result” is so useful when enumerating data. I don’t need a lot of memory to store lists of data but instead will process each item in the enumeration up to the point where the data gets aggregated.
Which in this case happens when I sort the data, as I need all data before I can sort it. If I don’t sort in-between, the data will only be aggregated when I call the Combine() method.
This is important to remember during data processing, as an exception in one of these methods can have unexpected consequences. That’s because all data before the faulty data will have been processed by the aggregator function. For FizzBuzz, that wouldn’t be a problem. But if one of the methods in the chain would write data to a database or file then it will have partially written some data up until it gets the exception.
So, when using enumerators, you might end up with garbage if you’re unaware of how data passes from one method to another. It feels unnatural for inexperienced developers yet it makes this FizzBuzz example so much more interesting…