Converting minutes into a readable string

I’d like to start posting some shorter entries. Hopefully that will be more conducive to making posts more often. I’m going to start tagging these as Short Code entries.

For this first one I came across a challenge on reddit about converting a quantity of minutes into a readable string, paraphrasing here:

Write a function that takes in a number of minutes as an integer and returns a string of the format “# years, # months, # days, # hours, #minutes”.

Requirements:

  1. The function should not show any zero amounts. “2 days, 0 hours” is not allowed
  2. For values less than a year, the function should return only the top two values over 1. This means that:
    • 2 months, 5 days, and 13 minutes would show up as “2 months, 5 days”
    • 1 month and 5 days shows up as “1 month, 5 days”.
    • 1 month and 1 minute shows as “1 month”.
  3. For values over a year the function should return the top three values (years, months, days).

The last requirement indicates a possibility that years and days might show, but not months.

I again wanted to use Python. One of the tutorials I looked at made use of dictionaries for a similar type of problem. That seemed like a good start.

After a little bit of testing and fiddling I ended up with my answer:

def minutes_to_time(minutes):
    types = {365*24*60: 'year',
             30*24*60: 'month',
             24*60: 'day',
             60: 'hour',
             1: 'minute'}

    remaining_minutes = minutes
    remaining_types = 3 if minutes > 365*24*60 else 2
    result = ''
    first = True
    remove_remaining = False
    for type_minutes in sorted(types, reverse=True):
        if remaining_minutes >= type_minutes and remaining_types > 0:
            type_name = types[type_minutes]
            count = remaining_minutes // type_minutes
            remaining_minutes %= type_minutes
            comma = '' if first else ', '
            plural = 's' if count > 1 else ''
            result += '{0}{1} {2}{3}'.format(comma, count, 
                                             type_name, plural)
            first = False
            remove_remaining = True
        if remove_remaining:
            remaining_types -= 1

    return result

When I first made the dictionary the strings names of the units of time were the keys and the integer values were the values. I found out soon that when dictionaries have strings as keys there’s no guarantee which order they’ll iterate over. Every time I ran it the loop iterated over the dictionary items in different order. That meant I needed to sort them before iterating over them. Sorting alphabetically by the name of the unit of time made no sense, so I had to swap the keys and values. Only after I finished did I notice that when the keys are integers the for loop appears to iterate over the dictionary in the order I created it even without sorting. (At least on OS X)

I’m sure there’s room for improvement in this code. To remove unnecessary code or do things in a “more pythonic” way. For example I could have gotten rid of the first and remove_remaining variables if I instead just checked the length of result instead of checking those variables. But I figured rather than keep going over it I’ll just post it as is.

Overall this was an interesting little challenge and I learned a couple things and feel like I understand Python a little better. Sometimes it’s nice to work on things that feel a bit more “computer sciencey”. Problems that are less about finding the right API or library call and more about cleverly using a handful of simpler operations in the correct way. At times programming can feel less like solving a problem and more like a scavenger hunt for the right object or API call. That’s not to say those things aren’t useful or I don’t like them, it just feels kind of nice to kick it back to basics sometimes.

Leave a Reply

Your email address will not be published. Required fields are marked *