Keys with same name with multiple Values in python - 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']}

Related

Itertools Groupby returns None when appending list to dict

I have the following code on which I'm grouping by a value in a particular dictionary:
from itertools import groupby
def group_owners(files):
# sort data before using groupby
files = dict(sorted(files.items()))
# create the groupby which will return an iterator containing a key (string) and a group which itself is a iterator
# in order to store this group iterator is necessary to initialize an empty data strcturure first
iterator = groupby(files,lambda x: files[x])
groups = []
return {k:groups.append(list(g)) for k,g in iterator}
if __name__ == "__main__":
files = {
'Input.txt': 'Randy',
'Code.py': 'Stan',
'Input2.txt': 'James',
'Output.txt': 'Randy'
}
print(group_owners(files))
This code returns me that:
{'Stan': None, 'Randy': None, 'James': None}
I was expecting a list in place of None with the groups for each key, in fact when I debug I can see that a list of lists is being created but at the end of program None is returned. I also would like to have only a flat list and not a list of lists.
My expected output is:
{'Stan': ['Code.py'], 'Randy': ['Input.txt','Output.txt'], 'James': 'Input2.txt'}
If I use instead:
{k:list(g) for k,g in iterator} what I get is:
{'Stan': ['Code.py'], 'Randy': ['Output.txt'], 'James': ['Input2.txt']}
I believe because of this fact, quoting form Python docs:
The returned group is itself an iterator that shares the underlying
iterable with groupby(). Because the source is shared, when the
groupby() object is advanced, the previous group is no longer visible.
So, if that data is needed later, it should be stored as a list:
https://docs.python.org/3/library/itertools.html#itertools.groupby
You can use this:
from collections import defaultdict
def group_owners_v2(files):
mp = defaultdict(list)
for key, value in files.items():
mp[value].append(key)
return dict(mp)
The output will be :
{'Randy': ['Input.txt', 'Output.txt'], 'Stan': ['Code.py'], 'James': ['Input2.txt']}
I just realised I was not sorting correctly by the value instead of the key in files:
Here is a solution that keeps files as dict and does not convert into a list of tuples, it is crucial to sort the data first when using groupby:
from itertools import groupby
def group_owners(files):
# sort dict
files = dict(sorted(files.items(),key = lambda x: x[1]))
# create groupby iterator
iterator = groupby(files, key=lambda x: files[x])
# return keys with their respective groups
return {k:list(g) for k,g in iterator}
if __name__=='__main__':
files = {
'Input.txt': 'Randy',
'Code.py': 'Stan',
'Input2.txt': 'James',
'Output.txt': 'Randy'
}
print(group_owners(files))
Output
{'James': ['Input2.txt'], 'Randy': ['Input.txt', 'Output.txt'], 'Stan': ['Code.py']}

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

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

python - nested dictionary as argument

Having following example dictionary:
dict = {
'key1': {
'nested_key1': {
'more_nested_key1': 'a_value'
}
},
'key2': {
'a_key': 'some_value'
}
}
I need a function that takes the dict and any number of nested keys and performs an action on the value. For instance:
def replace(dict, '['key1']['nested_key1']['more_nested_key1']', 'new_value')
would replace 'a_value' with 'new_value'. I am new to python, any idea how to achieve that?
You can pass a list of keys to the function, iterate through all but the last one of them until you get to the innermost dictionary, and then assign to the final key:
def replace(dict, keys, value):
d = dict
for key in keys[:-1]:
d = d[key]
d[keys[-1]] = value
Just iterate through the keys (except for the last), getting the next dict down each time. Then for the final key, set the item in the last dict to value
def replace(dct, *keys, value):
for key in keys[:-1]:
dct = dct[key]
dct[keys[-1]] = value
Usage:
replace(dict, 'key1', 'nested_key1', 'more_nested_key1', value='new_value')

Reordering an "dict" Object and shoveling multiple keys into an array of values (swap key-values in dictionary) | Ruby to Python [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 5 years ago.
Improve this question
This is my Ruby code. I want to convert this function to a Python 3 equivalent.
files = {
'Input.txt' => 'Randy',
'Code.py' => 'Stan',
'Output.txt' => 'Randy'
}
def group_by_owners(files)
files.each_with_object({}) { |(key, value), new_hash| (new_hash[value] ||= []) << key }
end
puts group_by_owners(files)
The Ruby result looks like this:
{"Randy" => ["Input.txt", "Output.txt"], "Stan" => ["Code.py"]}
Python would be:
{"Randy": ["Input.txt", "Output.txt"], "Stan": ["Code.py"]}
Here is what I have tried:
def group_by_owners(files):
new_dict = dict(zip(files.values(), (files.keys())))
print(new_dict)
Then I was trying to append the keys to an array.
def group_by_owners(files):
keys_array = []
new_dict = dict(zip(files.values(), keys_array.append((files.keys()))))
but I don't think that will work inside of a zip method.
In Python, your mentioned data structure is known as dictionary (dict in terms of code) and is syntactically represented as:
files = {
'Input.txt': 'Randy',
'Code.py': 'Stan',
'Output.txt': 'Randy'
}
In order to swap the key and values of your dict, you may use collections.defaultdict as:
from collections import defaultdict
swapped_dict = defaultdict(list)
for key, value in files.items():
swapped_dict[value].append(key)
where swapped_dict is a dict object holding the value:
{
'Randy': ['Output.txt', 'Input.txt'],
'Stan': ['Code.py']
}
Note: Ruby maintains the order, but in Python version < 3.6, the dict objects are unordered in nature. However, from Python version >= 3.6, dict objects are now ordered in nature.
For Python versions < 3.6, we have collections.OrderedDict which maintains the order in which keys are inserted. Here's an example to show the swapping of key/value pairs:
from collections import OrderedDict
# for maintaining the order, your initial list
# should also be of the type `OrderedDict`
old_dict = OrderedDict([('Input.txt', 'Randy'), ('Code.py', 'Stan'), ('Output.txt', 'Randy')])
for k, v in old_dict.items():
new_dict.setdefault(v, []).append(k)
# You may use `setdefault` on normal dictionaries too
which will return dict object as:
>>> new_dict
OrderedDict([('Randy', ['Input.txt', 'Output.txt']), ('Stan', ['Code.py'])])
It is just represented like this, you can access new_dict like a normal dictionary object.
def group_by_owners(files: dict): -> dict
res = {v: [] for v in files.values() }
for k, v in files.items():
res[v].append(k)
return res
Note: in Python dicts are unordered (until 3.7 version).
This is a dictionary version of files in Python:
files = {'Input.txt': 'Randy',
'Code.py': 'Stan',
'Output.txt' : 'Randy'}
files.items() returns:
dict_items([('Input.txt', 'Randy'), ('Code.py', 'Stan'), ('Output.txt', 'Randy')])
def group_by_owners(files):
result = dict() # empty dict
for k, v in files.items():
if v in result:
result[v].append(k) # Append to list value if the key is in result
else:
result[v] = [k] # Add key: value
return result
print(group_by_owners(files))
# {'Randy': ['Input.txt', 'Output.txt'], 'Stan': ['Code.py']}

Categories

Resources