1

As I ended up with many ugly if-elif's when comparing temperature to determine an adjective for the temperature, I thought that I can look for the temperature value in a list in a dictionary, where its key will be the corresponding adjective for the temperature:

def deternmine_temp(temp):

temps = {
    'FREEZING'  : [i for i in range(-20, 0)],
    'very cold' : [i for i in range(0, 6)],
    'cold'      : [i for i in range(6, 11)],
    'chilly'    : [i for i in range(11, 16)],
    'cool'      : [i for i in range(16, 21)],
    'warm'      : [i for i in range(21, 26)],
    'hot'       : [i for i in range(26, 31)],
    'very hot'  : [i for i in range(31, 36)],
    'dangerously hot' : [i for i in range(36, 40)],
    'extremely dangerously hot': [i for i in range(41, 46)]
}

temp = int(temp.replace('°', '').strip())

for word, values in temps.items():
    if temp in values:
        return word

This is a lot better than 7+ if-elif's, but I don't think it is very efficient, especially if temps had a lot more data (for example if I had a narrower range of values that correspond to an adjective).

What would be some ways to make this more efficient? Maybe some functions in the dictionary? Above is really the best I can think of.

  • 1
    This might interest you [use a range as a dictionary key in Python, what option do I have?](https://stackoverflow.com/questions/13464143/use-a-range-as-a-dictionary-key-in-python-what-option-do-i-have). Also, [Range as dictionary key in Python](https://stackoverflow.com/questions/39358092/range-as-dictionary-key-in-python/39358140) – Yoshikage Kira Jul 01 '21 at 04:30
  • Just use the `range` objects – juanpa.arrivillaga Jul 01 '21 at 04:50

3 Answers3

1

You need to store the ranges somehow, and since the ranges don't have the same interval you can't really shorten the dictionary initialization. You can extract the dictionary creation to a function, however you will still have to call it as many times as you have options.

You can however remove the list comprehension from the dictionary and replace the loop with next

def deternmine_temp(temp):
    temps = {
            'FREEZING': range(-20, 0),
            'very cold': range(0, 6),
            'cold': range(6, 11),
            'chilly': range(11, 16),
            'cool': range(16, 21),
            'warm': range(21, 26),
            'hot': range(26, 31),
            'very hot': range(31, 36),
            'dangerously hot': range(36, 40),
            'extremely dangerously hot': range(41, 46)
            }

    temp = int(temp.replace('°', '').strip())
    return next((word for word, values in temps.items() if temp in values), None)
Guy
  • 46,488
  • 10
  • 44
  • 88
0

If you decide to represent temperature as a floating-point number, your code in general will not work. To make sure your code works with non-integer temperatures (just in case), represent ranges as pairs of min-max values and use explicit comparison:

temps = {
    'FREEZING'  : (-20, 0),
    'very cold' : (0, 6),
    ....
}

for word, values in temps.items():
    if values[0] <= temp < values[1]:
        return word

You may as well use a list of tuples because you do not use dictionary-specific functionality:

temps = [
    ('FREEZING', -20, 0),
    ('very cold', 0, 6),
    ....
]

for word, value1, value2 in temps:
    if value1 <= temp < values2:
        return word

Finally, for consistency, you may define only the upper range boundary (which simultaneously is the lower boundary of the next range):

temps = [
    ('FREEZING', 0),
    ('very cold', 6),
    ....
    ('extremely dangerously hot': float('inf'))
]

for word, value in temps:
    if temp < value:
        return word
DYZ
  • 55,249
  • 10
  • 64
  • 93
0

This is closest to what i was looking for.

def switch(x):
    return {
            -20<x<=2: 'FREEZING',
            2<x<=5: 'very cold',
            5<x<=10: 'cold',
            10<x<=15: 'chilly',
            15<x<=22: 'cool',
            22<x<=25: 'warm',
            25<x<=30: 'hot',
            30<x<=33: 'very hot',
            33<x<=35: 'extremely hot',
            35<x<=40: 'dangerously hot'
            }[1]

print(switch(40))

output:

dangerously hot
  • This is interesting. I have not seen this kind of syntax before. How does it work? Is the part in braces {} a dictionary? If so it's a strange dictionary where the keys will be boolean. Is that even a thing? – bfris Jul 02 '21 at 23:56
  • 1
    Oh. I sort of figured it out. All of the keys will evaluate to True or False, which is also 1 or 0. So when function is called, you get a dictionary full of lots of False (0) keys and only one True (1) key. In this function, you could replace [1] with [True] and get the same result. Interesting.... – bfris Jul 03 '21 at 00:03
  • Right, me neither. Never seen this before, but looks about closest i found to what i wanted. – lizardowl5151 Jul 03 '21 at 00:57
  • 1
    FWIW, Python 3.10 is supposed to have a switch case statement built in. Only it will be called match case and the full blown official name is Structural Pattern Matching. – bfris Jul 03 '21 at 01:03