Reputation: 10547
Given a dictionary { k1: v1, k2: v2 ... }
I want to get { k1: f(v1), k2: f(v2) ... }
provided I pass a function f
.
Is there any such built in function? Or do I have to do
dict([(k, f(v)) for (k, v) in my_dictionary.iteritems()])
Ideally I would just write
my_dictionary.map_values(f)
or
my_dictionary.mutate_values_with(f)
That is, it doesn't matter to me if the original dictionary is mutated or a copy is created.
Upvotes: 357
Views: 557851
Reputation: 16
Here is how I handle the situation of wanting to mutate the values of a dictionary using map without creating a new data structure or second dictionary. This will work on any dictionary where all dictionary values can be mutated using the same function.
my_dict = {"a":1, "b":2, "c":3, "d":4}
times_ten = lambda x: x * 10
for key, value in zip(my_dict.keys(), map(times_ten, my_dict.values())):
my_dict[key] = value
print(my_dict) #Will print out values in my_dict as 10, 20, 30 ,40
The zip function will ensure that all the keys and the values match up correctly while still allowing you to separate the keys from the values so you can apply the map function to only the values. This solution has a time complexity of O(n) with a space complexity of O(1) since we create no new data structures doing this.
Upvotes: -1
Reputation: 1401
While my original answer missed the point (by trying to solve this problem with the solution to Accessing key in factory of defaultdict), I have reworked it to propose an actual solution to the present question.
Here it is:
class walkable_dict(dict):
def walk(self, callback):
try:
for key in self:
self[key] = callback(self[key])
except TypeError:
return False
return True
Usage:
>>> d = walkable_dict({ k1: v1, k2: v2 ... })
>>> d.walk(f)
The idea is to subclass the original dict to give it the desired functionality: "mapping" a function over all the values.
The plus point is that this dictionary can be used to store the original data as if it was a dict
, while transforming any data on request with a callback.
Of course, feel free to name the class and the function the way you want (the name chosen in this answer is inspired by PHP's array_walk()
function).
Note: Neither the try
-except
block nor the return
statements are mandatory for the functionality, they are there to further mimic the behavior of the PHP's array_walk
.
Upvotes: 3
Reputation: 1121196
There is no such function; the easiest way to do this is to use a dict comprehension:
my_dictionary = {k: f(v) for k, v in my_dictionary.items()}
Note that there is no such method on lists either; you'd have to use a list comprehension or the map()
function.
As such, you could use the map()
function for processing your dict as well:
my_dictionary = dict(map(lambda kv: (kv[0], f(kv[1])), my_dictionary.items()))
but that's not that readable, really.
(Note that if you're still using Python 2.7, you should use the .iteritems()
method instead of .items()
to save memory. Also, the dict comprehension syntax wasn't introduced until Python 2.7.)
Upvotes: 522
Reputation: 35
Suppose we have a dictionary d, we want to map a function over the values of d.
d = {'a': 1,'b': 2, 'c': 3}
def f(x):
return x + 1
result = dict(zip(d.keys(), map(f, d.values())))
print(result)
The result is
{'a': 2, 'b': 3, 'c': 4}
Upvotes: 0
Reputation: 1
def f(x): return x+2
bill = {"Alice": 20, "Bob": 10}
d = {map(lambda x: f(x),bill.values())}
print('d: ',dict(d))
Results
: d: {22: 12}
bills = {"Alice": [20, 15, 30], "Bob": [10, 35]}
d= {map(lambda v: sum(v),bills.values())}
g= dict(map(lambda v: (v[0],sum(v[1])),bills.items()))
# prints
print('d: ',dict(d))
print('g: ',g)
Results
d: {65: 45}
g: {'Alice': 65, 'Bob': 45}
Upvotes: 0
Reputation: 1321
These toolz are great for this kind of simple yet repetitive logic.
http://toolz.readthedocs.org/en/latest/api.html#toolz.dicttoolz.valmap
Gets you right where you want to be.
import toolz
def f(x):
return x+1
toolz.valmap(f, my_list)
Upvotes: 41
Reputation: 3893
Due to PEP-0469 which renamed iteritems() to items() and PEP-3113 which removed Tuple parameter unpacking, in Python 3.x you should write Martijn Pieters♦ answer like this:
my_dictionary = dict(map(lambda item: (item[0], f(item[1])), my_dictionary.items()))
Upvotes: 35
Reputation: 1002
To avoid doing indexing from inside lambda, like:
rval = dict(map(lambda kv : (kv[0], ' '.join(kv[1])), rval.iteritems()))
You can also do:
rval = dict(map(lambda(k,v) : (k, ' '.join(v)), rval.iteritems()))
Upvotes: 4
Reputation: 367
Just came accross this use case. I implemented gens's answer, adding a recursive approach for handling values that are also dicts:
def mutate_dict_in_place(f, d):
for k, v in d.iteritems():
if isinstance(v, dict):
mutate_dict_in_place(f, v)
else:
d[k] = f(v)
# Exemple handy usage
def utf8_everywhere(d):
mutate_dict_in_place((
lambda value:
value.decode('utf-8')
if isinstance(value, bytes)
else value
),
d
)
my_dict = {'a': b'byte1', 'b': {'c': b'byte2', 'd': b'byte3'}}
utf8_everywhere(my_dict)
print(my_dict)
This can be useful when dealing with json or yaml files that encode strings as bytes in Python 2
Upvotes: 1
Reputation: 1012
You can do this in-place, rather than create a new dict, which may be preferable for large dictionaries (if you do not need a copy).
def mutate_dict(f,d):
for k, v in d.iteritems():
d[k] = f(v)
my_dictionary = {'a':1, 'b':2}
mutate_dict(lambda x: x+1, my_dictionary)
results in my_dictionary
containing:
{'a': 2, 'b': 3}
Upvotes: 28