Loop and f-string

Raindrops
Raindrops in Python
def convert(number):
    sounds = ''
    drops = ("i", 3), ("a", 5), ("o", 7)

    for vowel, factor in drops:
        if number % factor == 0:
            sounds += f'Pl{vowel}ng'
    
    return sounds or str(number)

This approach loops through the drops tuple (although any iterable sequence(s) can be used), unpacking each vowel and factor. If the input number is evenly divisible by the factor (modulus == 0), the corresponding vowel is inserted into the f-string for that factor. The f-string is then concatenated to sounds string via +. Sounds is returned if it is not empty. Otherwise, a string version of the input number is returned.

This takes O(1) time and O(1) space.

It is a very efficient and clever way of building up the return string, since only one vowel is changing per 'drop'. However, it might take a moment for others reading the code to understand what exactly is going on. It also (may) create maintenance difficulties should there be future factors and sounds that do not conform to the pattern of only changing the vowel in the sound.

A much less exciting (but perhaps easier to maintain) rewrite would be to store the whole drop sound and build up the return string out of whole drops:

def convert(number):
    sounds = (3, 'Pling'), (5, 'Plang'), (7, 'Plong')
    output = ''
    
    for factor, sound in sounds:
       if number % factor == 0:
           output += sound
    
    return output or str(number)

This has the same time and space complexity as the first variation.

20th Nov 2024 · Found it useful?

Other Approaches to Raindrops in Python

Other ways our community solved this exercise
def convert(num):
    sounds = ''

    if num % 3 == 0: sounds += 'Pling'
    if num % 5 == 0: sounds += 'Plang'
    if num % 7 == 0: sounds += 'Plong'

    return sounds or str(num)
if statements

Use a series of if statements and string concatenation.

def convert(number):
    drops = ["Pling","Plang","Plong"]
    factors = [3,5,7]
    sounds = ''.join(drops[index] for
                     index, factor in
                     enumerate(factors) if (number % factor == 0))

    return sounds or str(number)
Sequence(s) with str.join()

Use one or more sequences and str.join() with a generator-expression.

def convert(number):
    threes = '' if number % 3 else 'Pling'
    fives =  '' if number % 5 else 'Plang'
    sevens = '' if number % 7 else 'Plong'

    return f'{threes}{fives}{sevens}' or str(number)
Truthy and Falsey Values with f-strings

Use ternaries to build words and an f-string to assemble the result.

def convert(number):
    sounds = {3: 'Pling', 5: 'Plang', 7: 'Plong'}

    results = ''.join(sounds[divisor] for
                      divisor in sounds.keys()
                      if number % divisor == 0)

    return results or str(number)
Dict and str.join()

Use a dict to hold factors:values and str.join() with a generator-expression.

from itertools import compress

def convert(number):
    sounds ='Pling','Plang','Plong'
    mask =  ((number % factor) == 0 for factor in (3,5,7))
    return ''.join(compress(sounds, mask)) or str(number)
itertools.compress

Use itertools.compress() with a list mask.

from functools import reduce

def convert(number):
    sounds = ('Pling','Plang','Plong')
    factors = ((number % factor) == 0 for factor in (3,5,7))
    result = reduce(lambda sound, item : sound + (item[0] * item[1]), zip(sounds, factors), '')

    return result or str(number)
functools.reduce

Use functools.reduce() and zip().