Visit the raindrops exercise on Exercism to read the full instructions and download the exercise files.
Dig Deeper
if statements
if statements
public static class Raindrops
{
public static string Convert(int number)
{
var drops = "";
if (number % 3 == 0) drops += "Pling";
if (number % 5 == 0) drops += "Plang";
if (number % 7 == 0) drops += "Plong";
return drops.Length > 0 ? drops : number.ToString();
}
}
- First,
drops is defined with var to be implicitly typed as a string, initialized as empty.
- The first
if statement uses the remainder operator to check if the is a multiple of 3.
If so, “Pling” is concatenated to drops using +.
- The second
if statement uses the remainder operator to check if the is a multiple of 5.
If so, “Plang” is concatenated to drops using +.
- The third
if statement uses the remainder operator to check if the is a multiple of 7.
If so, “Plong” is concatenated to drops using +.
A ternary operator is then used to return drops if it has more than 0 characters,
or it returns number.ToString().
Shortening
When the body of an if statement is a single line, both the test expression and the body could be put on the same line, like so
if (number % 3 == 0) drops += "Pling";
The C# Coding Conventions advise to write only one statement per line in the layout conventions section,
but the conventions begin by saying you can use them or adapt them to your needs.
Your team may choose to overrule them.
StringBuilder
StringBuilder
using System.Text;
public static class Raindrops
{
public static string Convert(int number)
{
var drops = new StringBuilder(15);
if (number % 3 == 0) drops.Append("Pling");
if (number % 5 == 0) drops.Append("Plang");
if (number % 7 == 0) drops.Append("Plong");
return drops.Length > 0 ? drops.ToString() : number.ToString();
}
}
We start by create a new StringBuilder instance:
var drops = new StringBuilder(15);
You may wonder why we're passing in `15` as the argument to the `StringBuilder` constructor?
Well, this sets the `StringBuilder`'s internal _capacity_, which is the length of the internal `char` array it allocates to build the string in.
When a call would have the string exceed the internal capacity, a new and bigger `char` array is allocated.
We know beforehand that our returning string is never longer than 15 characters (`"PlingPlangPlong"`), so by explicitly specifying the capacity we prevent any unneeded allocations, which is good for memory and thus performance.
We can then use the remainder operator (%) in an if-statement to check if the number is a multiple of 3, and if so, append "Pling" to the StringBuilder:
if (number % 3 == 0) drops.Append("Pling");
We then do the same for the other two sounds:
if (number % 5 == 0) drops.Append("Plang");
if (number % 7 == 0) drops.Append("Plong");
Finally, we use the ternary operator to see if the StringBuilder is not empty (drops.Length > 0), and if so, we convert the StringBuilder to a string via its ToString() method. If it does have length zero, we convert the input number to its string representation:
return drops.Length > 0 ? drops.ToString() : number.ToString();
Aggregate
Aggregate
using System.Linq;
public static class Raindrops
{
private static readonly (int, string)[] drips = { (3, "Pling"), (5, "Plang"), (7, "Plong") };
public static string Convert(int number)
{
var drops = drips.Aggregate("", (acc, drop) => number % drop.Item1 == 0 ? acc + drop.Item2 : acc);
return drops.Length > 0 ? drops : number.ToString();
}
}
- This solution begins by defining an array of tuples.
Each tuple has an
int and a string.
- The LINQ method Aggregate is called on the
drips array.
It passes the accumulator, which starts as an empty string, and each tuple to a lambda expression.
The lambda expression uses a ternary operator to test if number is evenly divisible by the int in the tuple.
If so, it returns the accumulator string concatenated with the string in the tuple.
If number is not evenly divisble by the int in the tuple, it simply returns the accumulator string.
- The result of all the iterations of
Aggregate is the value of the accumulator string.
That value is set to the drops string.
- A ternary operator is used to test the
drops string.
If the length of drops is greater than 0, then drops is returned from the function.
Otherwise number.ToString() is returned from the function.
Source: Exercism csharp/raindrops