2

I have a dictionary d already created. I want to change only the values of a certain type. For example's sake, I want to replace strings with their lengths.

The boring way to do this is with a for loop:

for k,v in d.items():
    if isinstance(v, str):
        d[k] = len(v)

But isn't looping slow?
It can be done with a comprehension. I think this is equivalent to the above:

d = {k: len(v) if isinstance(v, str) else v for k,v in d.items()}

I tried it with a map, but these don't work (apparently because I don't understand something about tuple unpacking in Python 3):

d = dict(map(lambda k,v: (k, len(v) if isinstance(v, str) else v), d.items()))
d = dict(map(lambda (k,v): (k, len(v) if isinstance(v, str) else v), d.items()))

This seems to work, but it's getting big and ugly:

dict(map(lambda kv: (kv[0], len(kv[1]) if isinstance(kv[1], str) else kv[1]), d.items()))

It seems like this kind of operation would be common, but I can't find specific answers.
What's the correct, Pythonic, and performant way to do it?

Jacktose
  • 709
  • 7
  • 21
  • 4
    Your `dict` comprehension is correct and imho is the pythonic way to achieve this. – Cory Kramer Mar 09 '18 at 22:46
  • 2
    The for loop doesn't create a new dictionary and replaces only those items that have a string as the value. The comprehension creates a new dictionary and sets every element, including those which have a non-string as the value. You ask if looping is slow, but when you need to deal with each item in a set of values there is no alternative. A comprehension is not magic; it is also a loop. – Paul Cornelius Mar 09 '18 at 22:55
  • @PaulCornelius Aww, not magic? No, I just thought maybe it was more optimized behind the scenes. So the `for` loop is maybe faster? Or uses less memory? – Jacktose Mar 09 '18 at 23:00
  • 1
    Other things being equal you would always prefer the comprehension to the for loop. But if you have a million items in your dictionary and only two of them are strings, the comprehension would perform 999,998 unnecessary copies whereas the for loop would simply replace 2 values. So a rule like *comprehension=fast, for loop=slow* is too simple. – Paul Cornelius Mar 09 '18 at 23:08
  • 1
    @P1h3r1e3d13 the for-loop will definitely use less memory in this case, indeed, it uses a constant amount of memory, vs the comprehension, which requires O(n) memory. But even if your loop were perfectly equivalent to the comprehension, i.e. it would build *a new dict instead of modifying an existing one*, then you can only expect marginal speed gains. Comprehensions should be preferred mostly for readability. – juanpa.arrivillaga Mar 09 '18 at 23:11
  • IOW: for-loops **are Pythonic**. – juanpa.arrivillaga Mar 09 '18 at 23:17

1 Answers1

2

You already have the answer, and CoryKramer already mentioned it.

d = {k: len(v) if isinstance(v, str) else v for k,v in d.items()}

This does it, and is the cleanest way.

A. Stewart
  • 36
  • 3