Is there a better way of re-arranging dictionary values? - python

I have the following dictionary:
files = {
'Input.txt': 'Randy',
'Code.py': 'Stan',
'Output.txt': 'Randy'}
I would like to return a dictionary where the keys are the names and where the corresponding value is a list of the filename:
{'Randy': ['Input.txt', 'output.txt'], 'Stan': ['Code.py']}
I managed to do it with 2 for loops:
def group_by_owners(files):
my_dict = {}
for value in files.values():
if value not in my_dict:
my_dict[value] = []
for key, value in files.items():
if value in my_dict.keys():
my_dict[value].append(key)
return my_dict
Is there a more efficient / elegant way of doing this?
Thanks

Option 1: defaultdict
Default dictionary with a default value of an empty list, so you append values to it.
This solution is preferable.
files = {
'Input.txt': 'Randy',
'Code.py': 'Stan',
'Output.txt': 'Randy'}
from collections import defaultdict
inv_map = defaultdict(list)
{inv_map[v].append(k) for k, v in files.items()}
# {'Randy': ['Input.txt', 'Output.txt'], 'Stan': ['Code.py']}
print(inv_map)
Option 2: Dictionary
files = {
'Input.txt': 'Randy',
'Code.py': 'Stan',
'Output.txt': 'Randy'}
inv_map = {}
for k, v in files.items():
inv_map[v] = inv_map.get(v, []) + [k]
# {'Randy': ['Input.txt', 'Output.txt'], 'Stan': ['Code.py']}
print(inv_map)

Here is my take on this. Using defaultdict to avoid creating the initial list, and just use append
from collections import defaultdict
def group_by_owners(files):
# Creates a dictionary that it's initial value is a list
# therefore you can just start using `append`
result = defaultdict(list)
for key, value in files.items():
result[value].append(key)
return result

Related

how to change tuple key into multilevel dict

I have a dictionary that looks like this:
d = {key1 : {(key2,key3) : value}, ...}
so it is a dictionary of dictionaries and in the inside dict the keys are tuples.
I would like to get a triple nested dict:
{key1 : {key2 : {key3 : value}, ...}
I know how to do it with 2 loops and a condition:
new_d = {}
for key1, inside_dict in d.items():
new_d[key1] = {}
for (key2,key3), value in inside_dict.items():
if key2 in new_d[key1].keys():
new_d[key1][key2][key3] = value
else:
new_d[key1][key2] = {key3 : value}
Edit: key2 values are not guaranteed to be unique. This is why I added the condition
It feels very unpythonic to me.
Is there a faster and/or shorter way to do this?
You could use the common trick for nesting dicts arbitrarily, using collections.defaultdict:
from collections import defaultdict
tree = lambda: defaultdict(tree)
new_d = tree()
for k1, dct in d.items():
for (k2, k3), val in dct.items():
new_d[k1][k2][k3] = val
If I understand the problem correctly, for this case you can wrap all the looping up in a dict comprehension. This assumes that your data is unique:
data = {"key1": {("key2", "key3"): "val"}}
{k: {keys[0]: {keys[1]: val}} for k,v in data.items() for keys, val in v.items()}

inversing a dictionary in python with duplicate values

I need to inverse a dictionary so that each old value will now be a key and the old keys will be the new values.
The trick is that there could be multiple values that are the same in the old dictionary so I need each value in the new dictionary to be a list, and if there were identical values in the old dictionary then they both will be in the list of the value of the new dictionary.
for example:
the dictionary {"python" : 1, "is" : 1, "cool" : 2}
would end up as: {1 : ["python", "is"], 2 : ["cool"]}
this is what I tried:
def inverse_dict(my_dict):
new_dict = {}
values_list = list(my_dict.values())
new_dict = new_dict.fromkeys(values_list)
for key in new_dict:
new_dict[key] = []
for old_key in my_dict:
new_dict[my_dict[old_key]] = list(new_dict[my_dict[old_key]]).append(old_key)
return new_dict
Would greatly appreciate any help with my approach (and better approaches to the problem) as I am very new to Python, thanks!
You can use dict.setdefault check if a key exists in the dictionary and if not, create new value (in this case empty list []):
d = {"python" : 1, "is" : 1, "cool" : 2}
reversed_d = {}
for k, v in d.items():
reversed_d.setdefault(v, []).append(k)
print(reversed_d)
Prints:
{1: ['python', 'is'], 2: ['cool']}
This can be more explicitly rewritten as:
d = {"python" : 1, "is" : 1, "cool" : 2}
reversed_d = {}
for k, v in d.items():
if v not in reversed_d:
reversed_d[v] = [k]
else:
reversed_d[v].append(k)
print(reversed_d)
You can use a defaultdict to avoid the pre-fill step
from collections import defaultdict
def inverse_dict(my_dict: dict):
new_dict = defaultdict(list)
for k, v in my_dict.items():
new_dict[v].append(k)
return new_dict
Though I prefer #azro's answer with the default dict, another solution is doing it with dictionary and list comprehensions.
It looks like this:
{value : [key for key in my_dict if my_dict[key] == value] for value in set(my_dict.values())}
What it does is runs over the values of the dictionary without duplicates - set(my_dict.values()).
It builds every value as a key (because it's on the left side of the ":").
And its value is a list of the keys that point to that value - [key for key in my_dict if my_dict[key] == value].

Populate a dictionary with for loop

I'd like to create a dictionary that would look like this:
{'area1': ['x1_area1', 'x2_area1'], 'area2': ['x1_area2', 'x2_area2']}
I'm trying to use a for loop:
dict_ ={}
keys =["area1","area2"]
for key in keys :
dict_ = {key : ["x1_"+ key,"x2_"+ key]}
dict_.update(dict_)
But I get this output:
{'area2': ['x1_area2', 'x2_area2']}
Try it:
keys =["area1","area2"]
dict_ = {}
for key in keys :
dict_[key] = ["x1_"+ key,"x2_"+ key]
dict_
Try this
res = {k: [f'{x}_{k}' for x in ('x1', 'x2')] for k in keys}
print(res)
Output:
{'area1': ['x1_area1', 'x2_area1'], 'area2': ['x1_area2', 'x2_area2']}
Inside your loop, you’re redefining dict_ and thus overwriting the existing values. Use a different variable name, or just inline its usage entirely:
dict_ = {}
keys = ["area1", "area2"]
for key in keys:
dict_.update({key : ["x1_" + key, "x2_" + key]})
This is taking over 100% of your code and just fixing the specific issues. There are better, more “Pythonic” solutions for this problem, which are explained in other answers.
The original code overrides the first value.
The following code will update without override:
keys =["area1","area2"]
dict_ = {}
for key in keys :
x = {key : ["x1_"+ key,"x2_"+ key]}
dict_.update(x)
# {'area1': ['x1_area1', 'x2_area1'], 'area2': ['x1_area2', 'x2_area2']}
print(dict_)
When looking at how to add items to a dictionaries, it seems like dict_[key] = "answer"
is the way to go.
dict_ = {}
keys = ["area1", "area2"]
for key in keys:
dict_[key] = ["x1_" + key, "x2_" + key]
print(dict_)
https://www.w3schools.com/python/python_dictionaries.asp

How do I create a dictionary from two lists in python while maintaining all key value information?

Example:
last_names = ['Bakir','Jose','Jose','Pierce']
university = ['Davis', 'Stanford', 'George Town', 'Berkeley']
Desire the Following
resulting_dictionary = {'Bakir':'Davis', 'Jose': ['Stanford', 'George Town'], 'Pierce':'Berkeley'}
I've tried the following
dictionary = {key:value for key, value in zip(last_names, university)}
But obtained the following:
{'Bakir': 'Davis', 'Jose': 'George Town', 'Pierce': 'Berkeley'}
Due to duplicate key value in last name list.
Thoughts?
Use defaultdict
from collections import defaultdict
d = defaultdict(list)
for k, v in zip(last_names, university):
d[k].append(v)
You can use the dict.setdefault method to initialize new keys with sub-lists:
dictionary = {}
for k, v in zip(last_names, university):
dictionary.setdefault(k, []).append(v)
You need to set the value to a list, and check whether the key already exists and append to it.
dictionary = {}
for key, value in zip(last_names, university):
if key in dictionary:
dictionary[key].append(value)
else:
dictionary[key] = [value]
This will make all the values lists. It's generally easier if you're consistent about your data format, not mixing strings and lists. So the result will be:
{'Bakir':['Davis'], 'Jose': ['Stanford', 'George Town']], 'Pierce':['Berkeley']}
Assuming, as the question seems to imply, that a list is not desired if only one value is present for a key, a simple flattening post-step can be used to achieve the specified result.
from collections import defaultdict
d = defaultdict(list)
for k, v in zip(last_names, university):
d[k].append(v)
# flatten single entry lists back to a string
d = { k: v[0] if len(v) == 1 else v for k, v in d.items() }
Try this:
my_dict = {}
for key, value in zip(last_names, university):
if key in my_dict:
old_value = my_dict[key]
if isinstance (old_value, str):
my_dict[key] = [old_value, value]
else:
old_value.add (value)
else:
my_dict[key] = value

Convert this list from stdin into dictionary

I am new to Python and need help with converting a list "lis" into a dictionary "dic". The list is generated by reading lines from stdin as follows-
lis = sys.stdin.readlines()
['San Jose\t200.82\n', 'California\t115.15\n', 'San Jose\t20.20\n', 'Texas\t300.10\n', 'San Jose\t100.50\n']
I have done the following to convert an item in list into key,value but dont know how to store this in a dictionary.
for item in lis:
k,v1 = item.split('\t')
v = float(v1)
print k,v
I would like to store this in a dictionary as follows
{'San Jose':200.82, 'California':115.15 .....etc}
Could you please show me how to do this efficiently? Later on I would like to load this dictionary into Pandas DataFrame.
You're almost there:
dic = {}
for item in lis:
k,v1 = item.split('\t')
v = float(v1)
print k,v
dic[k] = v
Of course, you could simplify this with a comprehension:
dic = {item.split('\t')[0]:float(item.split('\t')[1]) for item in lis}
It seems that you want multiple values associated with each key. For that, use a defaultdict:
from collections import defaultdict
dic = defaultdict(list)
for item in lis:
k,v1 = item.split('\t')
v = float(v1)
print k,v
dic[k].append(v)
Of course, you could just do the heavy-lifting yourself:
dic = {}
for item in lis:
k,v1 = item.split('\t')
v = float(v1)
print k,v
if k not in dic:
dic[k] = []
dic[k].append(v)
This should work -
dic = {}
for k,v in [e.strip().split("\t") for e in lis]:
dic[k] = v
#{'San Jose': '100.50', 'California': '115.15', 'Texas': '300.10'}
Or more succinctly -
dic = {k:v for k,v in [e.strip().split("\t") for e in lis]}
If you don't want to overwrite previous values -
dic = {}
for k,v in [e.strip().split("\t") for e in lis]:
if k not in dic:
dic[k] = v
If you want to have multiple values for the same key.
dic = {}
for k,v in [e.strip().split("\t") for e in lis]:
if k not in dic:
dic[k] = [v]
else:
dic[k].append(v)
'''
Output of the Dictionary.
{
'San Jose': ['200.82', '20.20', '100.50'],
'California': ['115.15'],
'Texas': ['300.10']
}
'''

Categories