Merge 2 dictionaries with higher value for each key [duplicate] - python

This question already has answers here:
keep highest value of duplicate keys in dicts
(6 answers)
Closed 2 years ago.
I have
dict1 = {a: 1, b: 2, c: 3}
dict2 = {b: 3, c: 2}
How do I merge dict1 and dict2 so that the result dict3 will have {a: 1, b: 3, c: 3}
I know we can merge like this dict3 = {**a, **b}, but is there a condition anywhere I have to write to make it work for my problem?

Here you go:
dict1 = {"a": 1, "b": 2, "c": 3}
dict2 = {"b": 3, "c": 2}
result = {}
for k in dict1.keys() | dict2.keys():
result[k] = max(dict1.get(k, float('-inf')), dict2.get(k,float('-inf')))
print(result)
I am using a get with default: dict1.get(k, DEFAULT) and joining the two keysets with the bitwise OR operator |.
Note that the default of float('-inf') means the result for
dict1 = {"a": -1, "b": 2, "c": 3}
dict2 = {"b": -5, "c": 2}```
becomes {'a': -1, 'b': 2, 'c': 3}.
While for the default of 0 you would get
{'b': 2, 'c': 3, 'a': 0}
Both ways could be considered equally valid responses.

You can merge the dict items into one sequence of tuples, sort them, and then use the dict constructor to create a new dict from the sorted sequence so that items of the same keys but with higher values will override those with lower values:
dict(sorted((*dict1.items(), *dict2.items())))

You could simply loop through them and compare the values, and use dict.setdefault(key, 0) to get around unset values.
dict1 = {'a': 1, 'b': 2, 'c': 3}
dict2 = {'b': 3, 'c': 2}
dict3 = {}
for d in dict1, dict2:
for k, v in d.items():
if v > dict3.setdefault(k, 0):
dict3[k] = v
print(dict3) # -> {'a': 1, 'b': 3, 'c': 3}
defaultdict(int) would also work but I wanted to avoid the import.

Related

Adding to a dictionary based on key and value from lists?

I have a dictionary defined as:
letters = {'a': 2, 'b': 1, 'c': 5}
I want to add values to this dictionary based on two lists: one which contains the keys and another which contains the values.
key_list = [a, c]
value_list = [2, 5]
This should give the output:
{a: 4, b: 1, c: 10}
Any ideas on how I can accomplish this? I am new to working with the dictionary structure so I apologise if this is extremely simple.
Thanks.
You can zip the two lists and then add to the dictionary as so;
letters = {'a': 2, 'b': 1, 'c': 5}
key_list = ['a', 'c']
value_list = [2, 5]
for k,v in zip(key_list, value_list):
letters[k] = letters.get(k, 0) + v
Using the dictionary's get() method as above allows you to add letters that aren't already in the dictionary.
for i in range(len(key_list)):
letters[key_list[i]] += value_list[i]
You can simply add or modify values from a dictionary using the key
For example:
letters = {'a': 2, 'b':1 , 'c': 5}
letters['a'] += 2
letters['c'] += 5
print(letters)
output = {'a': 4, 'b': 1, 'c': 10}

Subdivide python dict [duplicate]

This question already has answers here:
How do I create a nested dict from a dict by splitting the keys on a delimiter in python?
(4 answers)
Closed 1 year ago.
I want to write a method which takes a dict in the form of {a: 1, b-c: 2, b-d: 3, e-f-g: 4} and returns {a: 1, b: {c: 2, d: 3}, e: {f: {g: 4}}}. Basically split the keys containing - into subdicts. I've tried to do it iteratively and recursively but I got stuck. Any tips?
You can use collections.defaultdict with recursion:
from collections import defaultdict
d = {'a': 1, 'b.c': 2, 'b.d': 3, 'e.f.g': 4}
def group(d):
nd = defaultdict(list)
for [a, *b], c in d:
nd[a].append([b, c])
return {a:b[0][-1] if not any(j for j, _ in b) else group(b) for a, b in nd.items()}
result = group([[a.split('.'), b] for a, b in d.items()])
Output:
{'a': 1, 'b': {'c': 2, 'd': 3}, 'e': {'f': {'g': 4}}}

Python: issue trying to merge two dictionaries in which values must be added up

I'm extremely new to Python and stuck with a task of the online course I'm following. My knowledge of Python is very limited.
Here is the task: ''' Write a script that takes the following two
dictionaries and creates a new dictionary by combining the common keys
and adding the values of duplicate keys together. Please use For Loops
to iterate over these dictionaries to accomplish this task.
Example input/output:
dict_1 = {"a": 1, "b": 2, "c": 3} dict_2 = {"a": 2, "c": 4 , "d": 2}
result = {"a": 3, "b": 2, "c": 7 , "d": 2}
'''
dict_2 = {"a": 2, "c": 4 , "d": 2}
dict_3 = {}
for x, y in dict_1.items():
for z, h in dict_2.items():
if x == z:
dict_3[x] = (y + h)
else:
dict_3[x] = (y)
dict_3[z] = (h)
print(dict_3)
Wrong output:
{'a': 2, 'c': 3, 'd': 2, 'b': 2}
Everything is working up till the "else" condition.
I'm trying to isolate only the unique occurrences of both dictionaries, but the result actually overwrites what I added to the dictionary in the condition before.
Do you know a way to isolate only the single occurrences for every dictionary? I guess you could count them and add "if count is 1" condition, but I can't happen to make that work. Thanks!
dict_1 = {"a": 1, "b": 2, "c": 3}
dict_2 = {"a": 2, "c": 4 , "d": 2}
key_list = {*dict_1, *dict_2}
sum ={}
for key in key_list:
sum[key] = dict_1.get(key, 0) + dict_2.get(key, 0)
print(sum)
#{'a': 3, 'c': 7, 'd': 2, 'b': 2}
Not the most elegant or efficient solution, but an intuitive way would be to extract a list of the unique keys and then iterate over the new list of keys to extract and append the values from the two dictionaries.
dict_1 = {"a": 1, "b": 2, "c": 3}
dict_2 = {"a": 2, "c": 4 , "d": 2}
result = {}
# Extract the unique keys from both dicts
keys = set.union(set(dict_1.keys()), set(dict_2.keys()))
# Initialize the values of the result dictionary
for key in sorted(keys):
result[key] = 0
# Append the values of dict_1 and dict_2 to result if key is present
for key in keys:
if key in dict_1:
result[key] += dict_1[key]
if key in dict_2:
result[key] += dict_2[key]
print(result)
This will print: {'a': 3, 'b': 2, 'c': 7, 'd': 2}
Perhaps collections.defaultdict would be more suited to your purposes; when there's a value that it doesn't have, it just returns a default value that you assign to it and puts it in its "actual" dictionary. Then you can just convert it back to a normal dictionary with the dict() function.
from collections import defaultdict
dict_1 = {"a": 1, "b": 2, "c": 3}
dict_2 = {"a": 2, "c": 4 , "d": 2}
dict_3 = defaultdict(int) # provide 0 as the default value
for k, v in dict_1.items():
dict_3[k] += v
for k, v in dict_2.items():
dict_3[k] += v
print(dict(dict_3)) # convert back to normal dictionary
dict_1 = {"a": 1, "b": 2, "c": 3}
dict_2 = {"a": 2, "c": 4 , "d": 2}
dict_3={}
for key in dict_1:
if key in dict_2:
dict_3[key] = dict_2[key] + dict_1[key]
else:
dict_3[key]=dict_1[key]
for key in dict_2:
if key in dict_1:
dict_3[key] = dict_2[key] + dict_1[key]
else:
dict_3[key]=dict_2[key]
print(dict_3)
If you would like to avoid the use of a loop, you could use dictionary comprehension using get with default value 0 to avoid running into KeyError:
dict_1 = {"a": 1, "b": 2, "c": 3}
dict_2 = {"a": 2, "c": 4 , "d": 2}
dict_3 = {key: dict_1.get(key, 0) + dict_2.get(key, 0) for key in set(list(dict_1.keys())+list(dict_2.keys()))}
>>> {'c': 7, 'b': 2, 'd': 2, 'a': 3}
Though this is possibly unnecessarily advanced.
These changes to your original code produce your desired results.
dict_1 = {"a": 1, "b": 2, "c": 3}
dict_2 = {"a": 2, "c": 4 , "d": 2}
dict_3 = {}
for x, y in dict_1.items():
if x not in dict_2 and x not in dict_3:
dict_3[x] = y
for z, h in dict_2.items():
if x == z:
dict_3[x] = y + h
elif z not in dict_1 and z not in dict_3:
dict_3[z] = h
print(dict_3)
Prints:
{'a': 3, 'd': 2, 'b': 2, 'c': 7}
A more concise way would be to set dict_3 to dict_1 and then iterate over dict_2.
dict_3 = dict_1.copy()
for key, val in dict_2.items():
dict_3[key] = dict_3.get(key, 0) + val
The line dict_3.get(key, 0) gets the value for key in dict_3 if it exists in dict_3, otherwise, it supplies the value 0.

How do I get a unique set of values for a specific key in a list of dictionaries?

I'm using Python 3.8. If I want to get a unique set of values for an array of dictionaries, I can do the below
>>> lis = [{"a": 1, "b": 2}, {"a": 3, "b": 4}]
>>> s = set( val for dic in lis for val in dic.values())
>>> s
{1, 2, 3, 4}
However, how would I refine the above if I only wanted a unique set of values for the dictionary key "a"? In the above, the answer would be
{1, 3}
I'll assume that each dictionary in the array has the same set of keys.
You could simply do:
lis = [{"a": 1, "b": 2}, {"a": 3, "b": 4}]
# assuming the variable key points to what you want
key = 'a'
a_values = set(dictionary[key] for dictionary in lis)
I hope I've understood what you are looking for. Thanks.
You can do it like this:
lis = [{"a": 1, "b": 2}, {"a": 3, "b": 4}]
search_key = 'a'
s = set(val for dic in lis for key, val in dic.items() if key == search_key)
print(s)
#OUTPUT: {1, 3}
Use the dic.items() instead of dic.values() and check where the key is a.
Another way to do it to simplify the things:
lis = [{"a": 1, "b": 2}, {"a": 3, "b": 4}]
search_key = 'a'
s = set(dic.get(search_key) for dic in lis)
print(s)

Nesting dictionary algorithm

Suppose I have the following dictionary:
{'a': 0, 'b': 1, 'c': 2, 'c.1': 3, 'd': 4, 'd.1': 5, 'd.1.2': 6}
I wish to write an algorithm which outputs the following:
{
"a": 0,
"b": 1,
"c": {
"c": 2,
"c.1": 3
},
"d":{
"d": 4,
"d.1": {
"d.1": 5,
"d.1.2": 6
}
}
}
Note how the names are repeated inside the dictionary. And some have variable level of nesting (eg. "d").
I was wondering how you would go about doing this, or if there is a python library for this? I know you'd have to use recursion for something like this, but my recursion skills are quite poor. Any thoughts would be highly appreciated.
You can use a recursive function for this or just a loop. The tricky part is wrapping existing values into dictionaries if further child nodes have to be added below them.
def nested(d):
res = {}
for key, val in d.items():
t = res
# descend deeper into the nested dict
for x in [key[:i] for i, c in enumerate(key) if c == "."]:
if x in t and not isinstance(t[x], dict):
# wrap leaf value into another dict
t[x] = {x: t[x]}
t = t.setdefault(x, {})
# add actual key to nested dict
if key in t:
# already exists, go one level deeper
t[key][key] = val
else:
t[key] = val
return res
Your example:
d = {'a': 0, 'b': 1, 'c': 2, 'c.1': 3, 'd': 4, 'd.1': 5, 'd.1.2': 6}
print(nested(d))
# {'a': 0,
# 'b': 1,
# 'c': {'c': 2, 'c.1': 3},
# 'd': {'d': 4, 'd.1': {'d.1': 5, 'd.1.2': 6}}}
Nesting dictionary algorithm ...
how you would go about doing this,
sort the dictionary items
group the result by index 0 of the keys (first item in the tuples)
iterate over the groups
if there are is than one item in a group make a key for the group and add the group items as the values.
Slightly shorter recursion approach with collections.defaultdict:
from collections import defaultdict
data = {'a': 0, 'b': 1, 'c': 2, 'c.1': 3, 'd': 4, 'd.1': 5, 'd.1.2': 6}
def group(d, p = []):
_d, r = defaultdict(list), {}
for n, [a, *b], c in d:
_d[a].append((n, b, c))
for a, b in _d.items():
if (k:=[i for i in b if i[1]]):
r['.'.join(p+[a])] = {**{i[0]:i[-1] for i in b if not i[1]}, **group(k, p+[a])}
else:
r[b[0][0]] = b[0][-1]
return r
print(group([(a, a.split('.'), b) for a, b in data.items()]))
Output:
{'a': 0, 'b': 1, 'c': {'c': 2, 'c.1': 3}, 'd': {'d': 4, 'd.1': {'d.1': 5, 'd.1.2': 6}}}

Categories