Revisiting Raindrops in Python
By: Dan Eder
Tags: Refactoring, Python, Programming

A while ago, I laid out a solution to a Raindrops problem — similar to the classic FizzBuzz problem — in the Python track on exercism.io.

Here’s a quick recap of the problem:

Your task is to convert a number into a string that contains raindrop sounds corresponding to certain potential factors. A factor is a number that evenly divides into another number, leaving no remainder. The simplest way to test if a one number is a factor of another is to use the modulo operation The rules of raindrops are that if a given number:

• has 3 as a factor, add ‘Pling’ to the result.

• has 5 as a factor, add ‘Plang’ to the result.

• has 7 as a factor, add ‘Plong’ to the result.

• does not have any of 3, 5, or 7 as a factor, the result should be the digits of the number. Examples 28 has 7 as a factor, but not 3 or 5, so the result would be “Plong”. 30 has both 3 and 5 as factors, but not 7, so the result would be “PlingPlang”. 34 is not factored by 3, 5, or 7, so the result would be “34”.

And the solution:

def convert(number):

output = ‘’

if number % 3 == 0:

output += ‘Pling’ if number % 5 == 0:

output += ‘Plang’ if number % 7 == 0:

output += ‘Plong’ if output is ‘’:

return str(number)



return output

It’s okay. It’s a perfectly serviceable, effective solution. A workhorse. But it doesn’t exactly scale up. What if we want to eventually add dozens of factors? Will we have to write “if” statements for each of those? Putting these factors in a data structure then iterating through that would be more efficient. Also, creating strings in Python is relatively slow, as the program constantly recreates then destroys this data upon runtime. Appending invidual strings to a single list, then joining that list, would make the program run faster if we were to scale up eventually.

With those considerations in mind, let’s take a shot at refactoring this bad boy.

Some things to remember:

This solution does not work well with dozens of factors. It’s more efficient to put these factors into a data structure and iterate through it.

Creating strings in Python is relatively slow. It’s better not to do that often, especially with larger programs.

It’s possible to include an or condition in returns statements. You could write if not output , or even shorter return output or str(number) .

With that in mind, we get this:

def convert(number):

factors = []

output = ''

tu = {1:'Pling', 2:'Plang', 3:'Plong'}



if number % 3 == 0:

factors += tu[1]



if number % 5 == 0:

factors += tu[2]



if number % 7 == 0:

factors += tu[3]



return output.join(factors) or str(number)

By using a dictionary, we can iterate through and evaluate number in any number of ways. If number meets the requirements, we append the value of the dictionary at that particular key.

And if we wanted to add a dozen more requirements to this program, we could just add them to the dictionary and add new ‘if’ clauses.

Is it shorter? No. Does it follow best practices for economical data usage by not continually creating strings? You betcha. And does it set up a data structure that makes it simple to iterate through and evaluate? Indubitably.

Rather than creating strings every time we run the program, we instead append the single string values to a list and join them at once later.

Stay tuned for the thrilling conclusion of Raindrops, when we bust out the carving knife and start to do some trimming on those “if” statements…