Map a function to values of specified keys in dictionary - python

Is there a convenient way to map a function to specified keys in a dictionary?
Ie, given
d = {"a": 1, "b": 2, "c": 3}
would like to map a function, say f, to keys "a" and "c":
{"a": f(1), "b": 2, "c": f(3)}
EDIT
Looking for methods that will not update the input dictionary.

You can use a dictionary comprehension:
output_dict = {k: f(v) for k, v in d.items()}
Note that f(v) will be evaluated (called) immediately and its return values will be stored as the dictionary's values.
If you want to store the function and call it later (with the arguments already stored) you can use functools.partial:
from functools import partial
def f(n):
print(n * 2)
d = {"a": 1, "b": 2, "c": 3}
output_dict = {k: partial(f, v) for k, v in d.items()}
output_dict['b']()
# 4
If you only want specific keys mapped you can of course not use .items and just override those keys:
d['a'] = partial(f, d['a'])
or more generalized
keys = ('a', 'c')
for key in keys:
d[key] = partial(f, d[key])

Related

How do I change a key's value to that of another key's value if the value is the name of another key?

To explain what I mean, I'm adding keys and values to a dictionary but if a key in the dictionary has a value that's the name of another key, I want that key to be assigned the other key's value. For example, if I have dict1 = {"a": 100, "b": 200, "c": "a"}
is it possible to change the value of c to 100 (which is a's value)? So instead it would be
dict1 = {"a": 100, "b": 200, "c": 100} The code I have right now is obviously wrong and giving me an error but I was trying to type out what I thought would work
for x, y in dict1.items():
if dict1[x] == dict1[x]:
dict1[x] = dict1[y]
print(x, y)
You can use:
my_dict = {"a": 100, "b": 200, "c": "a"}
for k, v in my_dict.items():
if v in my_dict:
my_dict[k] = my_dict[v]
You can alternatively use a dict comprehension:
result = {
k: my_dict[v] if v in my_dict else v
for k, v in my_dict.items()
}
Output:
{'a': 100, 'b': 200, 'c': 100}

Recursive convert values to string using dictionary comprehension

Using dictionary comprehension is it possible to convert all values recursively to string?
I have this dictionary
d = {
"root": {
"a": "1",
"b": 2,
"c": 3,
"d": 4
}
}
I tried
{k: str(v) for k, v in d.items()}
But the code above turns the entire root value into string and I want this:
d = {"root": {"a": "1", "b": "2", "c": "3", "d": "4"}}
This is not a dictionary comprehension, but it works, it's just one line, and it's recursive!
(f := lambda d: {k: f(v) for k, v in d.items()} if type(d) == dict else str(d))(d)
It only works with Python 3.8+ though (because of the use of an assignment expression).
You could do a recursive solution for arbitrarily nested dicts, but if you only have 2 levels the following is sufficient:
{k: {k2: str(v2) for k2, v2 in v.items()} for k, v in d.items()}
Assuming that your given input was wrong and root's value was a dictionary, your code would somewhat work. You just need to add d['root'].items()
newDict = {k:{k: str(v) for k, v in d[k].items()} for k,v in d.items()}
output
{'root': {'a': '1', 'b': '2', 'c': '3', 'd': '4'}}
The following solution might not be using dictionary comprehension, but it is recursive and can transform dictionaries of any depth, I don't think that's possible using comprehension alone:
def convert_to_string(d):
for key, value in d.items():
if isinstance(value, dict):
convert_to_string(value)
else:
d[key] = str(value)
Found a simpler way to to achieve this using json module. Just made the following
import json
string_json = json.dumps(d) # Convert to json string
d = json.loads(string_json, parse_int=str) # This convert the `int` to `str` recursively.
Using a function
def dictionary_string(dictionary: dict) -> dict:
return json.loads(json.dumps(dictionary), parse_int=str, parse_float=str)
Regards

In Python, how do I find keys in an array of dictionaries where the values are the same?

I'm using Python 3.8. I have an array of dictionaries, all of which have the same keys ...
list_of_dicts = [{"a": 1, "b": 2}, {"a": 1, "b": "zz"}, {"a": 1, "b": "2"}]
How do I return a list of keys in which all the values are identical? For example, the above would be just
["a"]
since all three dicts have "a" = 1.
Here is my additional answer of #JaonHax.
array_of_dicts = [{"a": 1, "b": 2}, {"a": 1, "b": "zz", "c": "cc"}, {"a": 1, "b": "2"}]
def get_same_vals(dicts):
keys = []
for key in dicts[0].keys():
is_same = True
for each_dict in array_of_dicts:
if not key in each_dict or each_dict[key] != dicts[0][key]:
is_same = False
if is_same:
keys.append(key)
return keys
print(get_same_vals(array_of_dicts))
As suggested in other answers, create a master dictionary that groups each key, then check their uniqueness.
# all keys are the same, so get the list
keys = array_of_dicts[0].keys()
# collapse values into a single dictionary
value_dict = {k: set(d[k] for d in array_of_dicts) for k in keys}
# get list of all single-valued keys
print([k for k, v in value_dict.items() if len(v) == 1])
If you know for certain that they all have the same keys, you can iterate through their keys and the list like so:
array_of_dicts = [{"a": 1, "b": 2}, {"a": 1, "b": "zz"}, {"a": 1, "b": "2"}]
def get_same_vals(dicts):
keys = []
for key in dicts[0].keys():
is_same = True
for each_dict in dicts:
if each_dict[key] != dicts[0][key]:
is_same = False
if is_same:
keys.append(key)
return keys
print(get_same_vals(array_of_dicts))
# Prints ['a']
I apologise for the inefficient code; I didn't spend that long coding this up.
Here's a possible solution, it will work also in case that the dictionaries are differently structured (have different / extra keys):
array_of_dicts = [{"a": 1, "b": 2}, {"a": 1, "b": "zz"}, {"a": 1, "b": "2"}]
def is_entry_in_all_dicts(key, value):
identical_entries_found = 0
for dict in array_of_dicts:
if key in dict:
if dict[key] == value:
identical_entries_found += 1
if identical_entries_found == len(array_of_dicts):
return True
return False
result = []
for dict in array_of_dicts:
for key, value in dict.items():
if is_entry_in_all_dicts(key, value):
if key not in result:
result.append(key)
print(result)
Output
['a']
If every dictionary has the same keys, you can combine the values into sets and find the sets with one element:
[list(x.keys())[0] for x in [{k:set([e[k] for e in list_of_dicts])} for k in list_of_dicts[0]] if len(list(x.values())[0]) == 1]
Output:
['a']
Append your N dictionaries into a giant dictionary, and check for keys that have N identical values:
giant_dict = collections.defaultdict(list)
for k, v in (e for d in list_of_dicts for e in d):
giant_dict[k].append(v)
for k, v in giant_dict.items():
if len(v) == len(list_of_dicts) and all(e == v[0] for e in v):
print(k)
Here's a more succinct approach
from functools import reduce
array_of_dicts = [{"a": 1, "b": 2}, {"a": 1, "c": "zz"}, {"a": 1, "d": "2"}]
result = reduce(lambda a, b: a.intersection(b),list(map(lambda x: set(x.keys()),
array_of_dicts)))
Group your values into a set, then figure out which keys have a set with length 1:
>>> from collections import defaultdict
>>> list_of_dicts = [{"a": 1, "b": 2}, {"a": 1, "b": "zz"}, {"a": 1, "b": "2"}]
>>> grouped_values = defaultdict(set)
>>> for d in list_of_dicts:
... for k,v in d.items():
... grouped_values[k].add(v)
...
>>> [k for k,v in grouped_values.items() if len(v) == 1]
['a']

make a dict/json from string with duplicate keys Python

I have a string that could be parsed as a JSON or dict object. My string variable looks like this :
my_string_variable = """{
"a":1,
"b":{
"b1":1,
"b2":2
},
"b": {
"b1":3,
"b2":2,
"b4":8
}
}"""
When I do json.loads(my_string_variable), I have a dict but only the second value of the key "b" is kept, which is normal because a dict can't contain duplicate keys.
What would be the best way to have some sort of defaultdict like this :
result = {
"a": 1,
"b": [{"b1": 1, "b2": 2}, {"b1": 3, "b2": 2, "b4": 8}],
}
I have already looked for similar questions but they all deal with dicts or lists as an input and then create defaultdicts to handle the duplicate keys.
In my case I have a string variable and I would want to know if there is a simple way to achieve this.
something like the following can be done.
import json
def join_duplicate_keys(ordered_pairs):
d = {}
for k, v in ordered_pairs:
if k in d:
if type(d[k]) == list:
d[k].append(v)
else:
newlist = []
newlist.append(d[k])
newlist.append(v)
d[k] = newlist
else:
d[k] = v
return d
raw_post_data = '{"a":1, "b":{"b1":1,"b2":2}, "b": { "b1":3, "b2":2,"b4":8} }'
newdict = json.loads(raw_post_data, object_pairs_hook=join_duplicate_keys)
print (newdict)
Please note that above code depends on value type, if type(d[k]) == list. So if original string itself gives a list then there could be some error handling required to make the code robust.
Accepted answer is perfectly fine. I just wanted to show another approach.
So at first, you dedicate a list for values in order to easily accumulate next values. At the end, you call pop on the lists which have only one item. This means that the list doesn't have duplicate values:
import json
from collections import defaultdict
my_string_variable = '{"a":1, "b":{"b1":1,"b2":2}, "b": { "b1":3, "b2":2,"b4":8} }'
def join_duplicate_keys(ordered_pairs):
d = defaultdict(list)
for k, v in ordered_pairs:
d[k].append(v)
return {k: v.pop() if len(v) == 1 else v for k, v in d.items()}
d = json.loads(my_string_variable, object_pairs_hook=join_duplicate_keys)
print(d)
output:
{'a': 1, 'b': [{'b1': 1, 'b2': 2}, {'b1': 3, 'b2': 2, 'b4': 8}]}

Dictionary initializing in python

Is there any way to refer to the dict keys in the initialization body using one line and using dict keys "a" and "b"
Example:
def func(a,b)
return {"a":longComputation1(), "b":longComputation2(), sum_a_b:?????}
Please don't change semanthics of code. This just an example.
Use the function parameters:
>>> def func(a, b):
... return {"a": a, "b": b, "sum_a_b": a + b}
...
>>> func(1, 2)
{'a': 1, 'b': 2, 'sum_a_b': 3}
UPDATE Question changed after I posted the above code; Use jonrsharpe's solution.
Short answer: no.
This would have to be done over multiple lines:
def func():
d = {"a": longComputation1(),
"b": longComputation2()}
d.update(sum_a_b = d['a'] + d['b'])
return d
Use a function to create the dict and define the names of the keys for the sum in a key named sum:
def sum_dict(**kwargs):
result = {}
total = 0
sum_keys = kwargs["sum"]
del kwargs["sum"]
for key, value in kwargs.items():
val = value()
result[key] = val
if key in sum_keys:
total += val
result["sum_" + "_".join(sum_keys)] = total
return result
print(sum_dict(a=lambda: 3,b=lambda: 2,c=lambda: 14, sum=["a", "b"]))
# {'a': 3, 'c': 14, 'b': 2, 'sum_a_b': 5}
To access the keys from a not created dict is not possible.
Another way would be to create a own dict class.
I wonder what the practical application of this is, but if you mean that the key is dynamically constructed at initialisation time from other keys already present in the dictionary:
d = {"a":longComputation1(), "b":longComputation2()}
d['_'.join(['sum'] + d.keys())] = sum(d.values()) # assumes that keys are strings
>>> d
{'a': 10, 'b': 20, 'sum_a_b': 30} # assuming that longComputation1() == 10 and longComputation2() = 20
Sorry that it is not a single line (why the constraint??), but AFAIK you can't refer to the dict's keys during initialisation.

Categories

Resources