I read a code in a book 'Think Python'. This code gets stuck at the inverse[val].[key] with an error:
'str' object has no attribute 'append''
Which makes sense as inverse[val] contains a string object.
Here d is the input dictionary.
def invert_dict(d):
inverse = dict()
for key in d:
val = d[key]
if val not in inverse:
inverse[val] = [key]
else:
inverse[val].append(key)
return inverse
The input dictionary is {'a': 1, 'p': 1, 'r': 2, 't': 1, 'o': 1}
The expected output is {1: ['a', 'p', 't', 'o'], 2: ['r']}
How do I implement this, by modifying the given block of code?
You can use collections.defaultdict to create a dictionary of lists. Then append to dictionary values while iterating your input dictionary.
from collections import defaultdict
d_in = {'a': 1, 'p': 1, 'r': 2, 't': 1, 'o': 1}
d_out = defaultdict(list)
for k, v in d_in.items():
d_out[v].append(k)
print(d_out)
defaultdict(<class 'list'>, {1: ['a', 'p', 't', 'o'], 2: ['r']})
Your code can be improved by iterating keys and values simultaneously via dict.items, instead of iterating keys and manually extracting the value. In addition, your indentation is incorrect. After resolving these issues:
def invert_dict(d):
inverse = dict()
for key, val in d.items():
if val not in inverse:
inverse[val] = [key]
else:
inverse[val].append(key)
return inverse
try this:
def invert_dict(data):
inverse = {}
for key, value in data.items():
if value not in inverse:
inverse[value] = [key]
else:
inverse[value].append(key)
return inverse
A one-liner using reduce:
inverted_dict = reduce((lambda inverted_dict, key: inverted_dict.setdefault(dd[key], []).append(key) or inverted_dict), d, {})
Output:
{1: ['t', 'o', 'p', 'a'], 2: ['r']}
You can also follow a different approach in which you take all values from your dictionary and match each value with the keys that have this value in the initial dictionary:
def invert_dict(d):
values = set(d.values())
inverse = dict((v,[k for k in d.keys() if d[k]==v]) for v in values)
return inverse
inv = invert_dict({'a': 1, 'p': 1, 'r': 2, 't': 1, 'o': 1})
print(inv)
Output:
{1: ['a', 'p', 't', 'o'], 2: ['r']}
Related
I need to combine two dictionaries by their value, resulting in a new key which is the list of keys with the shared value. All I can find online is how to add two values with the same key or how to simply combine two dictionaries, so perhaps I am just searching in the wrong places.
To give an idea:
dic1 = {'A': 'B', 'C': 'D'}
dic2 = {'D': 'B', 'E': 'F'}
Should result in:
dic3 = {['A', 'D']: 'B', 'C': 'D', 'E': 'F'}
I am not sure why you would need such a data structure, you can probably find a better solution to your problem. However, just for the sake of answering your question, here is a possible solution:
dic1 = {'A':'B', 'C':'D'}
dic2 = {'D':'B', 'E':'F'}
key_list = list(dic2.keys())
val_list = list(dic2.values())
r = {}
for k,v in dic1.items():
if v in val_list:
i = val_list.index(v) #get index at value
k2 = key_list[i] #use index to retrive the key at value
r[(k, k2)] = v #make the dict entry
else:
r[k] = v
val_list = list(r.values()) #get all the values already processed
for k,v in dic2.items():
if v not in val_list: #if missing value
r[k] = v #add new entry
print(r)
output:
{('A', 'D'): 'B', 'C': 'D', 'E': 'F'}
You can't assign a list as a key in a python dictionary since the key must be hashable and a list is not an ashable object, so I have used a tuple instead.
I would use a defaultdict of lists and build a reversed dict and in the end reverse it while converting the lists to tuples (because lists are not hashable and can't be used as dict keys):
from collections import defaultdict
dic1 = {'A':'B', 'C':'D'}
dic2 = {'D':'B', 'E':'F'}
temp = defaultdict(list)
for d in (dic1, dic2):
for key, value in d.items():
temp[value].append(key)
print(temp)
res = {}
for key, value in temp.items():
if len(value) == 1:
res[value[0]] = key
else:
res[tuple(value)] = key
print(res)
The printout from this (showing the middle step of temp) is:
defaultdict(<class 'list'>, {'B': ['A', 'D'], 'D': ['C'], 'F': ['E']})
{('A', 'D'): 'B', 'C': 'D', 'E': 'F'}
If you are willing to compromise from 1-element tuples as keys, the second part will become much simpler:
res = {tuple(value): key for key, value in temp.items()}
Hi guys I have a dict id_dict where I have some keys. In this keys there are lists as values (sometimes only one item somtimes 4). (These are names of components)
my dict id_dict is created dynamically and the keys and values will always change.
S = {'G': ['crypto'], 'T': ['update', 'monitor', 'ipforum'], 'F': ['update'], 'M': ['crypto','update']}
R = {}
for key, value_list in S.items():
for value in value_list:
if value not in R:
R[value] = []
R[value].append(key)
print(R)
Output:
{'crypto': ['G', 'M'], 'update': ['T', 'F', 'M'], 'monitor': ['T'], 'ipforum': ['T']}
you are actually trying to assign the components list you're iterating in as a key of the dictionary, this is what will cause the error.
try by simply doing:
component_dict[component] = id
maybe try something like:
new_dic = {}
for k, v in dic.items():
for ele in v:
if ele not in new_dic:
new_dic[ele] = []
new_dic[ele].append(k)
new_dic
Note the append
It seems that u call components Instead of component and that cannot be a dict key, this si what causes the error,
In addition you didnt set it as a list and just assigned the value, you need to assign a list of it doesnt exist, and append to it if it does
So:
If dict_components.get(component):
dict_components[component].append(id)
else:
dict_components[component] = [id]
try this
from collections import defaultdict
d = {'G': ['crypto'], 'T': ['update', 'monitor', 'ipforum'], 'F': ['update'], 'M': ['crypto', 'update']}
d1 = defaultdict(list)
for k, v in d.items():
for x in v:
d1[x].append(k)
print(d1)
output
defaultdict(<class 'list'>, {'crypto': ['G', 'M'], 'update': ['T', 'F', 'M'], 'monitor': ['T'], 'ipforum': ['T']})
The easies way will be to use collections.defaultdict - which will save you from multiple tests
from collections import defaultdict
source = {'G': ['crypto'],
'T': ['update', 'monitor', 'ipforum'],
'F': ['update'],
'M': ['crypto','update']}
components = defaultdict(list)
for key, values_list in source.items():
for value in values_list:
components[value].append(key)
And the result will be
defaultdict(list,
{'crypto': ['G', 'M'],
'update': ['T', 'F', 'M'],
'monitor': ['T'],
'ipforum': ['T']})
As an alternative, you may create regular dict and use setdefault method to create lists
components = {}
for key, values_list in source.items():
for value in values_list:
components.setdefault(value, []).append(key)
I have a text file that reads:
a;b
a;c
a;d
b;h
c;e
e;f
e;g
e;j
f;b
g;d
h;b
h;e
i;d
i;e
but when I print it after making it into a dictionary
def read_graph(file_name):
graph = {}
for line in open(file_name):
if ";" in line:
key, val = map(str.strip, line.split(";"))
graph[key] = val
return dict(sorted(graph.items())))
It prints:
{'a': 'b', 'b': 'd', 'c': 'e', 'd': 'g', 'e': 'd', 'f': 'd'}
how do I make it where it prints the keys that repeat?
I assume for this you'd want to use a list of strings instead of a single string as the value, otherwise your dictionary will keep replacing the value for the same key.
Instead of:
{'a': 'b'}
You would probably want a structure such as:
{'a': ['b','c','d']}
Using your function:
def read_graph(file_name):
graph = {}
for line in open(file_name):
if ";" not in line: continue
key, val = line.strip().split(';')
if key not in graph: graph[key] = list()
if val not in graph[key]: graph[key].append(val)
return dict(sorted(graph.items()))
read_graph('file.txt')
{'a': ['b', 'c', 'd'], 'c': ['e'], 'b': ['h'], 'e': ['f', 'g', 'j'], 'g': ['d'], 'f': ['b'], 'i': ['d', 'e'], 'h': ['b', 'e']}
Dictionaries in python (and every other language I know) have unique values for each key, and will overwrite them when you put a new value in for an existing key.
Consider a different kind of data structure, like a set of tuples, e.g.
{('a','b'), ('a','c'), ...}
Or, as it looks like you are making a graph, a dictionary where the values are lists of vertices instead of individual vertices, e.g.
{'a':['b','c'],...}
To make the set of tuples, replace the line
graph[key] = val
with
graph.append((key, val))
To make a dictionary-to-lists, use
if key in graph:
graph[key].append(val)
else:
graph[key] = [val]
Hope this helps!
You cannot because that is a dictionary, and it is not allowed to have two same keys or it would ambiguous. You could group by key.
def read_graph(file_name):
graph = {}
for line in open(file_name):
if ";" in line:
key, val = map(str.strip, line.split(";"))
if key not in graph:
graph[key] = [val]
else:
graph[key].append(val)
return dict(sorted(graph.items())))
So now you have for every key, an array with its values.
Since you seem to be working with a graph structure, I would recommend you look at the NetworkX package for Python. They have pre-built graph data-structures for you to use and many algorithms that can operate on them.
import networkx as nx
graph = nx.Graph()
with open(file_name) as f: # This closes the file automatically when you're done
for line in f:
if ";" in line:
source, dest = map(str.strip, line.split(";"))
graph.add_edge(source, dest)
In case you still want to use vanilla Python only:
Python's dictionaries can only have one value per key. To store multiple values for a single key, you have to store your keys in a list of values.
my_dict = {
'a': ['b', 'c', 'd'],
'b': ['h'],
...
}
I have a list that contains dictionaries with Letters and Frequencies. Basically, I have 53 dictionaries each for every alphabet (lowercase and uppercase) and space.
adict = {'Letter':'a', 'Frequency':0}
bdict = {'Letter':'b', 'Frequency':0}
cdict = {'Letter':'c', 'Frequency':0}
If you input a word, it will scan the word and update the frequency for its corresponding letter.
for ex in range(0, len(temp)):
if temp[count] == 'a': adict['Frequency']+=1
elif temp[count] == 'b': bdict['Frequency']+=1
elif temp[count] == 'c': cdict['Frequency']+=1
For example, I enter the word "Hello", The letters H,e,l,l,o is detected and its frequencies updated. Non zero frequencies will be transferred to a new list.
if adict['Frequency'] != 0 : newArr.append(adict)
if bdict['Frequency'] != 0 : newArr.append(bdict)
if cdict['Frequency'] != 0 : newArr.append(cdict)
After this, I had the newArr sorted by Frequency and transferred to a new list called finalArr. Below is a sample list contents for the word "Hello"
{'Letter': 'H', 'Frequency': 1}
{'Letter': 'e', 'Frequency': 1}
{'Letter': 'o', 'Frequency': 1}
{'Letter': 'l', 'Frequency': 2}
Now what I want is to transfer only the key values to 2 seperate lists; letterArr and numArr. How do I do this? my desired output is:
letterArr = [H,e,o,l]
numArr = [1,1,1,2]
Why don't you just use a collections.Counter? For example:
from collections import Counter
from operator import itemgetter
word = input('Enter a word: ')
c = Counter(word)
letter_arr, num_arr = zip(*sorted(c.items(), key=itemgetter(1,0)))
print(letter_arr)
print(num_arr)
Note the use of sorted() to sort by increasing frequency. itemgetter() is used to reverse the sort order so that the sort is performed first on the frequency, and then on the letter. The sorted frequencies are then separated using zip() on the unpacked list.
Demo
Enter a word: Hello
('H', 'e', 'o', 'l')
(1, 1, 1, 2)
The results are tuples, but you can convert to lists if you want with list(letter_arr) and list(num_arr).
I have a hard time understanding your data structure choice for this problem.
Why don't you just go with a dictionary like this:
frequencies = { 'H': 1, 'e': 1, 'l': 2, 'o': 1 }
Which is even easier to implement with a Counter:
from collections import Counter
frequencies = Counter("Hello")
print(frequencies)
>>> Counter({ 'H': 1, 'e': 1, 'l': 2, 'o': 1 })
Then to add another word, you'd simply have to use the updatemethod:
frequencies.update("How")
print(frequencies)
>>> Counter({'l': 2, 'H': 2, 'o': 2, 'w': 1, 'e': 1})
Finally, to get your 2 arrays, you can do:
letterArr, numArr = zip(*frequencies.items())
This will give you tuples though, if you really need lists, just do: list(letterArr)
You wanted a simple answer without further todo like zip, collections, itemgetter etc. This does the minimum to get it done, 3 lines in a loop.
finalArr= [{'Letter': 'H', 'Frequency': 1},
{'Letter': 'e', 'Frequency': 1},
{'Letter': 'o', 'Frequency': 1},
{'Letter': 'l', 'Frequency': 2}]
letterArr = []
numArr = []
for i in range(len(finalArr)):
letterArr.append(finalArr[i]['Letter'])
numArr.append(finalArr[i]['Frequency'])
print letterArr
print numArr
Output is
['H', 'e', 'o', 'l']
[1, 1, 1, 2]
Sorry about the question repost...I should have just edited this question in the first place. Flagged the new one for the mods. Sorry for the trouble
Had to re-write the question due to changed requirements.
I have a dictionary such as the following:
d = {'a': [4, 2], 'b': [3, 4], 'c': [4, 3], 'd': [4, 3], 'e': [4], 'f': [4], 'g': [4]}
I want to get the keys that are associated with the smallest length in the dictionary d, as well as those that have the maximum value.
In this case, the keys with the smallest length (smallest length of lists in this dictionary) should return
'e, 'f', 'g'
And those with the greatest value(the sum of the integers in each list) should return
'b' 'c'
I have tried
min_value = min(dict.itervalues())
min_keys = [k for k in d if dict[k] == min_value]
But that does not give me the result I want.
Any ideas?
Thanks!
Your problem is that your lists contain strings ('2'), and not integers (2). Leave out the quotes, or use the following:
min_value = min(min(map(int, v) for v in dct.values()))
min_keys = [k for k,v in d.items() if min_value in map(int, v)]
Similarily, to calculate the keys with the max length:
max_length = max(map(len, dct.values()))
maxlen_keys = [k for k,v in d.items() if max_length == len(v)]
Also, it's a bad idea to use dict as a variable name, as doing so overshadows the built-in dict.
You can use min() with a key= argument, and specify a key function that compares the way you want.
d = {'a': ['1'], 'b': ['1', '2'], 'c': ['2'], 'd':['1']}
min_value = min(d.values())
min_list = [key for key, value in d.items() if value == min_value]
max_len = len(max(d.values(), key=len))
long_list = [key for key, value in d.items() if len(value) == max_len]
print(min_list)
print(long_list)
Notes:
0) Don't use dict as a variable name; that's the name of the class for dictionary, and if you use it as a variable name you "shadow" it. I just used d for the name here.
1) min_value was easy; no need to use a key= function.
2) max_len uses a key= function, len(), to find the longest value.
How about using sorting and lambdas?
#!/usr/bin/env python
d = {'a': ['1'], 'b': ['1', '2'], 'c': ['8', '1'], 'd':['1'], 'e':['1', '2', '3'], 'f': [4, 1]}
sorted_by_sum_d = sorted(d, key=lambda key: sum(list(int(item) for item in d[key])))
sorted_by_length_d = sorted(d, key=lambda key: len(d[key]))
print "Sorted by sum of the items in the list : %s" % sorted_by_sum_d
print "Sorted by length of the items in the list : %s" % sorted_by_length_d
This would output:
Sorted by sum of the items in the list : ['a', 'd', 'b', 'f', 'e', 'c']
Sorted by length of the items in the list : ['a', 'd', 'c', 'b', 'f', 'e']
Be aware I changed the initial 'd' dictionary (just to make sure it was working)
Then, if you want the item with the biggest sum, you get the last element of the sorted_by_sum_d list.
(I'm not too sure this is what you want, though)
Edit:
If you can ensure that the lists are always going to be lists of integers (or numeric types, for that matter, such as long, float...), there's not need to cast strings to integers. The calculation of the sorted_by_sum_d variable can be done simply using:
d = {'a': [1], 'b': [1, 2], 'c': [8, 1], 'd':[1], 'e':[1, 2, 3], 'f': [4, 1]}
sorted_by_sum_d = sorted(d, key=lambda key: sum(d[key]))
I've found such a simple solution:
min_len = len(min(d.values(), key=(lambda value: len(value)))) # 1
min_keys = [key for i, key in enumerate(d) if len(d[key]) == min_len] # ['e', 'f', 'g']