Modify dict values inplace

28,064

Solution 1

You may find multiply is still faster than dividing

d2 = {k: v * 0.5 for k, v in d.items()}

For an inplace version

d.update((k, v * 0.5) for k,v in d.items())

For the general case

def f(x)
    """Divide the parameter by 2"""
    return x / 2.0

d2 = {k: f(v) for k, v in d.items()}

Solution 2

You can loop through the keys and update them:

for key, value in d.items():
    d[key] = value / 2

Solution 3

Should work for you:

>>> d = {'a':2.0, 'b':3.0}
>>> for x in d:
...     d[x]/=2
... 
>>> d
{'a': 1.0, 'b': 1.5}

Solution 4

>>> d = { 'a': 2, 'b': 3 }
>>> {k: v / 2.0 for k, v in d.items()}
{'a': 1.0, 'b': 1.5}
Share:
28,064
Qortex
Author by

Qortex

Creating a startup (Qontrol) to help companies manage their cybersecurity risk with a dynamic and easy-to-use SaaS.

Updated on July 09, 2022

Comments

  • Qortex
    Qortex almost 2 years

    I would like to apply a function to values of a dict inplace in the dict (like map in a functional programming setting).

    Let's say I have this dict:

    d = { 'a':2, 'b':3 }
    

    I want to apply the function divide by 2.0 to all values of the dict, leading to:

    d = { 'a':1., 'b':1.5 }
    

    What is the simplest way to do that?

    I use Python 3.

    Edit: A one-liner would be nice. The divide by 2 is just an example, I need the function to be a parameter.

  • Qortex
    Qortex about 11 years
    This creates a new dictionary right? So with a huge dict, it would be resource intensive. Is there a way to just modify the values without losing the advantage of having the already built dict? (hence the inplace in my question)
  • Admin
    Admin about 11 years
    @Mic Don't worry about performance unless there is a performance problem (and I'm not saying there isn't, but justify the assertion) - or keeping the same object is required for other semantic reasons. That being said, I entirely agree with jamylak - "inplace" (mutating) and "functional" (immutable) are fairly orthogonal, depending on where the line is drawn.
  • jamylak
    jamylak about 11 years
    @Mic True but when I heard the word 'functional' I didn't think you would tell me this.
  • mgilson
    mgilson about 11 years
    This'll work in python2.7 also other than maybe doing integer division sometimes (which is easily fixed with a judiciously placed period).
  • mgilson
    mgilson about 11 years
    Note that you could also do d.update((k,v/2.) for k,v in d.items()) if you wanted it in-place.
  • jamylak
    jamylak about 11 years
    @mgilson You can submit that if you want, I don't really like it that much
  • Jonathan Vanasco
    Jonathan Vanasco about 11 years
    You should also note in the answer that this creates a new list , and there is little reason not to create a new list. This is what I would do , but this doesn't answer the actual question.
  • jamylak
    jamylak about 11 years
    @JonathanVanasco There are no new lists being created
  • abarnert
    abarnert about 11 years
    @mgilson: I think d.update({k: v/2.0 for k, v in d.items()}) is more readable (and more obviously related to jamylak's answer).
  • Jonathan Vanasco
    Jonathan Vanasco about 11 years
    sorry, i meant dict. your answer creates a new one and leaves the original one intact.
  • Jonathan Vanasco
    Jonathan Vanasco about 11 years
    and now the question has been edited so there is a d2, not the origina d. so nevermind, though the question title should be changed.
  • mgilson
    mgilson about 11 years
    @abarnert -- Yes, but I thought that OP was trying to save memory for one reason or another...
  • abarnert
    abarnert about 11 years
    You really think multiplying will be significantly faster than dividing? Maybe in PyPy, where all of the costs but the arithmetic are shrunk down to the point where it makes a difference, but in CPython, I'll bet you can't even detect the difference…
  • jamylak
    jamylak about 11 years
    @abarnert Then you would be creating a new dictionary so you may as well use my method... mgilson's one has the benefit of using a generator
  • abarnert
    abarnert about 11 years
    Also, I think passing the dict comprehension to d2 instead of a generator expression generating tuples may be more readable in this case (even if it does "wastefully" create a new dictionary).
  • mgilson
    mgilson about 11 years
    Anyway, I don't really think I would use that. That's why I made it a comment rather than an answer. (I prefer the for loop).
  • mgilson
    mgilson about 11 years
    As a side note, I don't actually like dict comprehensions in python3.x code for a two main reasons -- 1) Your code isn't backward compatible with python2.6, 2) dict comprehensions look just a little bit too similar to set comprehensions. (I also don't really like set literals for the same reason)
  • abarnert
    abarnert about 11 years
    %timeit results. CPython 2.7.2: 1.11us vs. 1.12us, or 222us vs. 223us for a large (1000-item random) dictionary. CPython 3.3.0: 785ns vs. 778ns, or 160us vs. 158us. So, multiplying is not faster than dividing, as I suspected. (I haven't tested PyPy yet because I broke my ipython-pypy…)
  • abarnert
    abarnert about 11 years
    OK, fixed… PyPy 1.9.0: 309ns vs. 329ns, or 94.4us vs. 94.8us. So, with PyPy, with tiny dictionaries, multiplying is slightly faster. With CPython, or with non-tiny dictionaries, they're equivalent.
  • abarnert
    abarnert about 11 years
    @mgilson: I love dict comprehensions. Set literals are a different story, but only because {} is not the empty set, which is about as big an irregularity as you can have in a set display format…
  • abarnert
    abarnert about 11 years
    @mgilson: I agree. Paraphrasing Guido off python-ideas: If you want mutating code, write it explicit with for. If you must have it in one line, just put the body after the colon.
  • mgilson
    mgilson about 11 years
    This could be a 1-liner as well (if you cheat a little) -- for key,value in d.items(): d[key] = value/2
  • abarnert
    abarnert about 11 years
    I don't think explicitly using items and value in this case is saving anything in readability (or performance, if that matters); Ishpeck's answer looks simpler (except for your better variable name).
  • abarnert
    abarnert about 11 years
    @mgilson: If Guido suggests it, it doesn't count as cheating, right? (Of course he suggests it as "If you must have it on one line…")
  • mgilson
    mgilson about 11 years
    @abarnert -- Apparently guido also once tweeted that you can use triple-quoted strings as a block comment ... Apparently I don't agree with him on everything
  • Qortex
    Qortex about 11 years
    @mgilson indeed, I can't have two copies of this dict or my VM basically dies. Hence the inplace in the title.
  • John La Rooy
    John La Rooy about 11 years
    @abarnert, well I started by saying "You may find..." :)
  • abarnert
    abarnert about 11 years
    @Mic: Are you sure that's really a problem? And, if so, why did you tag this "functional-programming"?
  • abarnert
    abarnert about 11 years
    @mgilson: Of course not. But when he agrees with you, he's always right! That's the beauty of authority. :)
  • abarnert
    abarnert about 11 years
    @Mic: That's really kind of misleading, if you understand what dict.update does. You have to figure out that the generator expression is effectively generating a lazy dictionary with the exact same keys as the original before you realize that it's changing each value in-place, while with for key in d: d[key] /= 2 it's immediately apparent what you're doing.