appending dicts based on keys- overwriting previous values - python

In python 3.8, I have the following dict:
dict_base= dict()
Then I am generating another dict in my operation
dict_1={'Sedan': 'Accord',
'SUV': 'Pilot'
}
dict_base.append(dict1)
This appends dict1 to dict_base.
Next I have a second dict
dict_2={'Sedan': 'Camry',
'SUV': 'Highlander'
}
dict_base.append(dict_2)
What I am expecting is to see a result as follows:
dict_base={'Sedan': ['Accord', 'Camry'],
'SUV': ['Pilot', 'Highlander']
}
What I am seeing is
dict_base={'Sedan':'Camry',
'SUV':'Highlander'
}
So, I am not sure how to append correctly...as it appears my append operation is overwriting the previous values.
Thanks!

This is likely a good usecase for collections.defaultdict. You can't magically append each value of one dict containing strings to the corresponding keys of a dict containing lists. Python has no idea what you want to do, so you have to spell it out.
Make a dictionary that defaults to lists for missing elements:
dict_base = collections.defaultdict(list)
For each element of dict_1, dict_2, etc, tell python what to do with the values:
for k, v in dict_1.items():
dict_base[k].append(v)
Do the same thing for dict_2, or better yet, write a function:
def append_base(base, d):
for k, v in d.items():
base[k].append(v)
You can also do this with conventional dictionaries, especially if you know the list of keys ahead of time:
keys = ['Sedan', 'SUV']
dict_base = {k: [] for k in keys}

dict_1 = {'Sedan': 'Accord', 'SUV': 'Pilot'}
dict_2 = {'Sedan': 'Camry', 'SUV': 'Highlander'}
You can use itertools with collections:
import itertools
import collections
result = collections.defaultdict(list)
for key, value in itertools.chain(dict_1.items(), dict_2.items()):
result[key].append(value)
print(dict(result))
# Outputs {'Sedan': ['Accord', 'Camry'], 'SUV': ['Pilot', 'Highlander']}
You can use collections:
import collections
result = collections.defaultdict(list)
for d in (dict_1, dict_2):
for key, value in d.items():
result[key].append(value)
print(dict(result))
# Outputs {'Sedan': ['Accord', 'Camry'], 'SUV': ['Pilot', 'Highlander']}
Hell, you can even use nothing:
result = {}
for d in (dict_1, dict_2):
for key, value in d.items():
result.setdefault(key, []).append(value)
print(result)
# Outputs {'Sedan': ['Accord', 'Camry'], 'SUV': ['Pilot', 'Highlander']}

Instead of defining dict_base as dict, you can use collections.defaultdict
It can be this way -
from collections import defaultdict
dict_1={'Sedan': 'Accord',
'SUV': 'Pilot'
}
dict_2={'Sedan': 'Camry',
'SUV': 'Highlander'
}
dict_base = defaultdict(list)
for d in (dict_1, dict_2):
for key, value in d.items():
dict_base[key].append(value)

Related

How to create a reverse dictionary that takes in account repeated values?

I am trying to create a function that takes in a dictionary and returns a reverse of it while taking care of repeated values. That is, if the original dictionary would be
original_dict = {'first': ['a'], 'second': ['b', 'c'], 'third': ['d'], 'fourth': ['d']}
the function should return
{'a': ['first'], 'b': ['second'], 'c': ['second'], 'd': ['third', 'fourth']}
I've written
def reversed_dict(d):
new_dict = {}
for keys,values in d.items():
new_dict[values]=keys
but when I try it out with the original dictionary, I get an error "unhashable type: 'list'" when I try out the function. Are there any hints what might be causing it?
You have to iterate over the values in the list as well:
def reversed_dict(d):
new_dict = {}
for keys,values in d.items():
for val in values:
new_dict.setdefault(val, []).append(keys)
return new_dict
You have to iterate over the values and add them as keys. You also have to take into account the possibility that you may have already added a value as a key.
def reversed_dict(d):
new_dict = {}
for keys,values in d.items():
for v in values:
if v in new_dict:
new_dict[v].append(keys)
else:
new_dict[v] = [keys]
return new_dict
Use collections.defaultdict:
from collections import defaultdict
def reversed_dict(d):
new_dict = defaultdict(list)
for key, values in d.items():
for value in values:
new_dict[value].append(key)
return new_dict
The problem with your approach is you're using the entire list as the key of the dictionary. Instead you need to iterate over the list (i.e. for value in values: in the code above.)
defaultdict just makes it simpler to read.
You are getting this error because any of your original_dict values is a mutable type which is, as the error suggests, an unhashable type thus not
avalid candidate for a key in the reversed_dict.
You can workaround this problem by type-checking and casting mutable types into an immutable equivalent, e.g. a list into a tuple.
(also I find dict comp a way more elegant and concise approach):
def reversed_dict(d):
return {v if not isinstance(v, list) else tuple(v): k for k, v in d.items()}

How to reverse dictionary items and list keys grouped by common values [duplicate]

This question already has answers here:
Dictionary comprehension for swapping keys/values in a dict with multiple equal values
(3 answers)
Closed 2 years ago.
I have a dictionary that I want to group by the common values:
init_dict = {'00001': 'string1', '00002': 'string2', '00003': 'string1', '00004': 'string3', '00005': 'string2'}
I want to create a new dictionary that groups the values and lists the keys like this:
new_dict = {'string1': ['00001', '00003'], 'string2':['00002', '00004'], 'string3': ['00004']}
I tried many things and this is the closest I can get.
lookup = 'string1'
all_keys = []
for k, v in init_dict.items():
if v == lookup:
all_keys.append(k)
print(all_keys)
This produces the first list: ['00001', '00003'] so I thought I could somehow loop through a list of lookup values but can't since I'm working with strings. Is there a way to do this and is there a way that is relatively efficient because my initial dictionary has 53,000 items in it. Any help would be much appreciated as I've been trying different things for hours.
Use a defaultdict, specifying a list as default argument, and append the corresponding values from the dictionary:
from collections import defaultdict
d = defaultdict(list)
for k,v in init_dict.items():
d[v].append(k)
print(d)
defaultdict(list,
{'string1': ['00001', '00003'],
'string2': ['00002', '00005'],
'string3': ['00004']})
You can use defaultdict
result = defaultdict(list)
for k, v in init_dict.items():
result[v].append(k)
or itertools.groupby
result = {k: [x[0] for x in v] for k, v in
groupby(sorted(init_dict.items(), key=lambda kv: kv[1]), key=lambda kv: kv[1])}
You can also use a normal dict (instead of defaultdict):
new_dict = {}
for key, val in init_dict.items():
if val in new_dict:
new_dict[val].append(key)
else:
new_dict[val] = []
new_dict[val].append(key)
Output:
new_dict = {'string1': ['00001', '00003'],
'string2': ['00002', '00005'],
'string3': ['00004']}

How to switch values and keys with python dict?

My input is:
files = {
'Input.txt': 'Randy',
'Code.py': 'Stan',
'Output.txt': 'Randy'
}
I want the output as:
{'Randy':['Input.txt','Output.txt'], 'Stan':['Code.py']}
Basicly it's the other direction of this switch key and values in a dict of lists
This is what I tried:
dictresult= {}
for key,value in files.items():
dictresult[key]=value
dictresult[value].append(key)
But it doesn't work. I get KeyError: 'Randy'
Here's simple approach where we iterate over keys and values of your original dict files and create list for each value to append to it all keys corresponding to that value.
files = {
'Input.txt': 'Randy',
'Code.py': 'Stan',
'Output.txt': 'Randy'
}
dictresult= {}
for k, v in files.items():
if v not in dictresult:
dictresult[v] = [k]
else:
dictresult[v].append(k)
print(dictresult) # -> {'Randy': ['Output.txt', 'Input.txt'], 'Stan': ['Code.py']}
There are a few problems with your code.
Let's review:
Firstly, you're getting key error because you're trying to append a value to key that doesn't exist. Why? because in you earlier statement you added a value to dict[key] and now you're trying to access/append dict[value].
dictresult[key]=value
You're assigning value to newly generated key, without any check. every new value will overwrite it.
dictresult[value].append(key)
Then you're trying to append a new value to a string using a wrong key.
You can achieve what you want by the following code:
d = {}
for key,value in files.items():
if value in d:
d[value].append(key)
else:
d[value] = [key]
print(d)
It will output:
{'Randy': ['Input.txt', 'Output.txt'], 'Stan': ['Code.py']}
How/why it works?
Let's review:
The if condition checks if that key is already present in the dictionary. When iterated over a dictionary, it returns it's keys only, not key value pairs unlike dict.items()
If the key is there we simply append the current value to it.
In other case, where that key is not present, we add a new key to dictionary but we do it by casting it to a list, otherwise a string would be inserted as a value, not a list and you won't be able to append to it.
Try using defaultdict -
from collections import defaultdict
dictresult= defaultdict(list)
for key,value in files.items():
dictresult[value].append(key)
This will assume there is an empty list in every item in the dictionary so the append won't fail
You can check if the value as a key exist in dictresult
Like
dictresult= {}
for key,value in files.items():
if not value in dictresult: dictresult [value]=[]
dictresult[value].append(key)
output = {}
for key, value in files.items():
output[value] = output.get(value, []) + [key]
print(output)
# {'Randy':['Input.txt','Output.txt'], 'Stan':['Code.py']}
Here are two ways how you can do it.
from collections import defaultdict
files = {"Input.txt": "Randy", "Code.py": "Stan", "Output.txt": "Randy"}
expected = {"Randy": ["Input.txt", "Output.txt"], "Stan": ["Code.py"]}
# 1st method. Using defaultdict
inverted_dict = defaultdict(list)
{inverted_dict[v].append(k) for k, v in files.items()}
assert inverted_dict == expected, "1st method"
# 2nd method. Using regular dict
inverted_dict = dict()
for key, value in files.items():
inverted_dict.setdefault(value, list()).append(key)
assert inverted_dict == expected, "2nd method"
print("PASSED!!!")

Create a new dict of first n values (and keys) from dictionary - Python

I have a dictionary:
{'my_account': [45010045, 43527907, 45147474, 35108100, 45159973],
'your_account': [38966628, 28171579, 39573751, 41359842, 42445236],
'his_account': [44822460, 45010045, 39276850, 39896128, 45265335]
}
I want to keep the first 2 elements of every key, so the result would look like:
{'my_account': [45010045, 43527907],
'your_account': [38966628, 28171579],
'his_account': [44822460, 45010045]
}
Is there any way to achieve this? Thanks.
using dictionary comprehension
my_dict = {'my_account': [45010045, 43527907, 45147474, 35108100, 45159973],
'your_account': [38966628, 28171579, 39573751, 41359842, 42445236],
'his_account': [44822460, 45010045, 39276850, 39896128, 45265335]
}
new_dict = {k:v[:2] for k,v in my_dict.items()}
# {'my_account': [45010045, 43527907], 'your_account': [38966628, 28171579], 'his_account': [44822460, 45010045]}
Just slice-delete the values.
for v in D.itervalues():
del v[2:]

how to combine the common key and join the values in the dictionary python

I have one list which contain a few dictionaries.
[{u'TEXT242.txt': u'work'},{u'TEXT242.txt': u'go to work'},{u'TEXT1007.txt': u'report'},{u'TEXT797.txt': u'study'}]
how to combine the dictionary when it has the same key. for example:
u'work', u'go to work'are under one key:'TEXT242.txt', so that i can remove the duplicated key.
[{u'TEXT242.txt': [u'work', u'go to work']},{u'TEXT1007.txt': u'report'},{u'TEXT797.txt': u'study'}]
The setdefault method of dictionaries is handy here... it can create an empty list when a dictionary key doesn't exist, so that you can always append the value.
dictlist = [{u'TEXT242.txt': u'work'},{u'TEXT242.txt': u'go to work'},{u'TEXT1007.txt': u'report'},{u'TEXT797.txt': u'study'}]
newdict = {}
for d in dictlist:
for k in d:
newdict.setdefault(k, []).append(d[k])
from collections import defaultdict
before = [{u'TEXT242.txt': u'work'},{u'TEXT242.txt': u'go to work'},{u'TEXT1007.txt': u'report'},{u'TEXT797.txt': u'study'}]
after = defaultdict(list)
for i in before:
for k, v in i.items():
after[k].append(v)
out:
defaultdict(list,
{'TEXT1007.txt': ['report'],
'TEXT242.txt': ['work', 'go to work'],
'TEXT797.txt': ['study']})
This technique is simpler and faster
than an equivalent technique using dict.setdefault()

Categories

Resources