4

I wrote a function to convert all keys in a dictionary to lowercase:

def lower_dict_keys(some_dict):
    """Convert all keys to lowercase"""
    result = {}
    for key, value in some_dict.items():
        if type(key) == str:
            result[key.lower()] = value
        else:
            result[key] = value
    return result

So far so good. Then I thought, hm, this would be more elegant in a dictionary comprehension, so this is what I came up with:

def lower_dict_keys2(some_dict):
    return {key.lower(): value for key, value in some_dict.items() if type(key) == str}

This only works if all keys are strings. I there are numeric keys, they get dropped:

d1 = {'A':'foo', 'b':'bar', 1:'zip'}

print(lower_dict_keys(d1))

>>>{'a': 'foo', 'b': 'bar', 1: 'zip'}

print(lower_dict_keys2(d1))

>>>{'a': 'foo', 'b': 'bar'}

So my question is: is it possible to write this function as a dictionary comprehension?

jpp
  • 159,742
  • 34
  • 281
  • 339
uwain12345
  • 356
  • 2
  • 21

2 Answers2

5

The correct translation of your code to a comprehension is:

{key.lower() if type(key) == str else key: value for key, value in some_dict.items()}

using a conditional expression to determine the key for each key-value pair.

Using if after the for loop filters on elements; those elements that don't pass the if test are not included in the output.

You also want to avoid using type(...) is, use isinstance() instead; subclasses of str will support the lower() method too:

{key.lower() if isinstance(key, str) else key: value for key, value in some_dict.items()}
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
0

If you expect your keys to ordinarily be strings, a more appropriate solution would be to use a try / except clause.

Here we catch AttributeError, which will occur only for non-string elements.

def lower_dict_keys(some_dict):
    """Convert all keys to lowercase"""
    result = {}
    for key, value in some_dict.items():
        try:
            result[key.lower()] = value
        except AttributeError:
            result[key] = value
    return result

d1 = {'A':'foo', 'b':'bar', 1:'zip'}

lower_dict_keys(d1)

# {'a': 'foo', 'b': 'bar', 1: 'zip'}

Unfortunately, this will not work with a dictionary comprehension, since statements are not permitted in comprehensions and PEP-463 was rejected.

jpp
  • 159,742
  • 34
  • 281
  • 339