How to switch values and keys with python dict? - python

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!!!")

Related

Get a dictionary having a specific key value pair from a complex dictionary

I hit a specific scenario where I want to get the dictionary which contains specific value out of a complex dictionary.
For example consider the below dictionary
a = {"tire1": {"source": "PEP","dest": "host1"},"tire6":{"source": "REP","dest":"host2"}}
If the value of dest host1 matches then the function should return {"tire1": {"source": "PEP","dest": "host1"} of type dictionary.
EDIT: If it matches multiple same values then it should returns multiple matching dictionary in a single dictionary
Thanks
You Can do something like this by using dictionary comprehension
final_dict = {key: value for key,value in a.items() if value["dest"] == "host1" }
You could define a function with a results list, iterate on the keys and values of dictionary a, append to the results list any values (sub-dictionaries) where the 'dest' key equals 'host1', and then return the list of results.
def func(a: dict):
results = [] # define a list of results to return
for key, value in a.items(): # iterate on keys values
if value['dest'] == 'host1':
results.append(a[key]) # append that sub-dict to the results
return results
This would return:
result = func(a)
print(results)
----
{
"tire1":
{
"source": "PEP",
"dest": "host1"
}
}
You can simply write a loop to match the specified value.
In your case, the code is following:
dicts = {"tire1": {"source": "PEP","dest": "host1"},"tire6":{"source": "REP","dest":"host2"}}
search_value = 'host1'
for key, values in dicts.items():
for value in values.values():
if value == search_value:
print(f'The Key is {key} and', f'The dictionary are {values}')
This will return the matched dictionary with the key. The result will be:
The Key is tire1 and The dictionary are {'source': 'PEP', 'dest': 'host1'}
A more robust option could be:
a = {"tire1": {"source": "PEP", "dest": "host1"},
"tire6": {"source": "REP", "dest": "host2"}}
result = {k: v for k, v in a.items() if v.get('dest') == 'host1'}
print(result)
...in case there are sub-dictionaries where the 'dest' key is absent

Keys with same name with multiple Values in python

I am practicing myself in python online and have come across this question.
https://www.testdome.com/questions/python/file-owners/11846?visibility=1&skillId=9
I dont know how to solve this. When I interchange the Key and Value and I am getting the output with only unique Key-value pairs as below
{'Stan': 'Code.py', 'Randy': 'Input.txt'}
Kindly suggest how to resolve this.
One way is to put the value in the list for "Randy", as in dictionary we can't have multiple keys of same name. Here is the solution for the same,
class FileOwners:
#staticmethod
def group_by_owners(files):
d={}
for i in files:
if files[i] in d:
d[files[i]].append(i)
else:
d[files[i]]=[i]
return d
files = {
'Input.txt': 'Randy',
'Code.py': 'Stan',
'Output.txt': 'Randy'
}
print(FileOwners.group_by_owners(files))
You can't have the same key in a dict. For your example, use owner as key and the value will be a list of files.
Something like this:
class FileOwners:
#staticmethod
def group_by_owners(files):
result = {}
for _f in list(files.keys()):
if not files[_f] in result.keys():
result[files[_f]] = []
result[files[_f]].append(_f)
return result
files = {
'Input.txt': 'Randy',
'Code.py': 'Stan',
'Output.txt': 'Randy'
}
print(FileOwners.group_by_owners(files))
Hint: instead of using a single item as the contents of the dict, use a list. Then, using a for loop, gradually populate that list with items:
output = {}
for key, value in files.items():
if value in output:
output[value].append(key)
else:
output[value] = [key]
return output
You can see here that we first create a new dictionary, output, and then we put the key-value pairs from files into output as value-key.
The key here is that each value of output is a list, not just a single item. That's why we check if the value already exists in output before adding it: if it does, then we just add the new key to the list. If not, we create a new list, with just one element: key.
A dict can't have duplicate keys, which is why when you just interchange a dict (e.g. output = {val:key for key, val in files.items()}) the code doesn't work as intended. The first value for Randy gets overwritten by the second value for Randy.
You can use defaultdict for this:
from collections import defaultdict
class FileOwners:
#staticmethod
def group_by_owners(files):
d = defaultdict(list)
for k, v in files.items():
d[v].append(k)
return dict(d)
files = {
'Input.txt': 'Randy',
'Code.py': 'Stan',
'Output.txt': 'Randy'
}
print(FileOwners.group_by_owners(files))
# {'Randy': ['Input.txt', 'Output.txt'], 'Stan': ['Code.py']}

Strip nested dict of non zero values

I'm trying to strip a nested dict (only 1 level deep eg: some_dict = {'a':{}, b:{}} all all non-zero and none values.
However I'm not sure who to reassemble the dict properly, the below gives me a key error.
def strip_nested_dict(self, some_dict):
new_dict = {}
for sub_dict_key, sub_dict in some_dict.items():
for key, value in sub_dict.items():
if value:
new_dict[sub_dict_key][key] = value
return new_dict
You need to create the nested dictionary before accessing it:
for sub_dict_key, sub_dict in some_dict.items():
new_dict[sub_dict_key] = {} # Add this line
for key, value in sub_dict.items():
# no changes
(In order for new_dict[sub_dict_key][key] to work, new_dict must be a dictionary, & new_dict[sub_dict_key] also has to be a dictionary.)
This worked. Shame you can't just assign a nested value without having to create an empty for for each key first.
def strip_nested_dict(self, some_dict):
new_dict = {}
for sub_dict_key, sub_dict in some_dict.items():
new_dict[sub_dict_key] = {}
for key, value in sub_dict.items():
if value:
new_dict[sub_dict_key][key] = value
return new_dict

accesing data in a dictionary python

so i have this code:
dic1 = { "data1":1, "data2":2, "data3":3}
dic2 = { "data1":4, "data2":5, "data3":6}
dic3 = { "data1":7, "data2":8, "data3":9}
data = [dic1, dic3, dic2]
how can i access the data in the dictionaries from a function if the input is the list?
so if i have a for:
for x in data:
if x == "dic1":
print dic1["data1"]
print dic1["data2"]
elif x == "dic2":
print dic2["data1"]
and so on......
that will work but only because i know that those dictionaries exist but if another dictionary is created that method will obviously not work, so how can i do it.
Use
for x in data:
for k, v in x.items():
print k, v
So the key here is to use the items method to access to the dictionary's elements.
The first loop iterates over dictionaries, the second loop iterates over keys of dictionary considered by the first loop. Then you print every element:
for dictionary in data:
for key in dictionary:
print dictionary[key]

How to append a value to a not yet existing key?

I have a dictionary like this:
dct = {'one': 'value',
'two': ['value1','value2','value1'],
'three':['otherValue1','otherValue2','otherValue1'],
'dontCareAboutThisKey':'debug'}
I need to remove duplicate values from the lists. I wrote a function to do this:
no_dups = {}
for keys in dct:
if isinstance(dct[keys], list) and keys != 'dontCareAboutThisKey':
for value in dct[keys]:
if value not in no_dups.values():
no_dups[keys].append(value)
else:
no_dups[keys] = dct[keys]
I'm checking if value of the current key is a list. If no, it just 'copy' key to no_dups dictionary. If it is a list and not a key that I don't care about (there are no duplicates for sure) - it should check if current value already exists in no_dups.values() and append it to current key. Problem is that I'm getting an error:
KeyError: 'two:'
I know it's because I'm trying to add a value to non existing key but I have no idea how to deal with this and make it work.
I think the best way to deal with adding the key and appending at the same time is with dicts' setdefault() method:
no_dups.setdefault(keys,[]).append(value)
But rather than that, you can do this in a more neat way like this:
#remove duplicates
no_dups = {k:list(set(v)) if isinstance(v, list) and k != 'dontCareAboutThisKey' else v
for k,v in dct.items()} # or dct.iteritems() if using python2.x
That hack will, for key value combinations that pass the if test, convert the list into a set (removing duplicates) and then in a list again. For other key value combinations it will leave it intact.
dct = {'one': 'value',
'two': ['value1','value2','value1'],
'three':['otherValue1','otherValue2','otherValue1'],
'dontCareAboutThisKey':'debug'}
set(dct) returns a set, which is a list without duplicates:
for key, value in dct.items():
if not isinstance(value, basestring):
dct[key] = set(value)
If you need a new dictionary you could do:
new_dct = {}
for key, value in dct.items():
if not isinstance(value, basestring):
new_dct[key] = set(value)
else:
new_dct[key] = value
If You want to remove duplicates, just change You list to set, with set() function:
https://docs.python.org/2/tutorial/datastructures.html#sets
It automatically gives You unique set, then You can always change it back to list.

Categories

Resources