python error 'dict' object has no attribute: 'add' - python

I wrote this code to perform as a simple search engine in a list of strings like the example below:
mii(['hello world','hello','hello cat','hellolot of cats']) == {'hello': {0, 1, 2}, 'cat': {2}, 'of': {3}, 'world': {0}, 'cats': {3}, 'hellolot': {3}}
but I constantly get the error
'dict' object has no attribute 'add'
how can I fix it?
def mii(strlist):
word={}
index={}
for str in strlist:
for str2 in str.split():
if str2 in word==False:
word.add(str2)
i={}
for (n,m) in list(enumerate(strlist)):
k=m.split()
if str2 in k:
i.add(n)
index.add(i)
return { x:y for (x,y) in zip(word,index)}

In Python, when you initialize an object as word = {} you're creating a dict object and not a set object (which I assume is what you wanted). In order to create a set, use:
word = set()
You might have been confused by Python's Set Comprehension, e.g.:
myset = {e for e in [1, 2, 3, 1]}
which results in a set containing elements 1, 2 and 3. Similarly Dict Comprehension:
mydict = {k: v for k, v in [(1, 2)]}
results in a dictionary with key-value pair 1: 2.

x = [1, 2, 3] # is a literal that creates a list (mutable array).
x = [] # creates an empty list.
x = (1, 2, 3) # is a literal that creates a tuple (constant list).
x = () # creates an empty tuple.
x = {1, 2, 3} # is a literal that creates a set.
x = {} # confusingly creates an empty dictionary (hash array), NOT a set, because dictionaries were there first in python.
Use
x = set() # to create an empty set.
Also note that
x = {"first": 1, "unordered": 2, "hash": 3} # is a literal that creates a dictionary, just to mix things up.

I see lots of issues in your function -
In Python {} is an empty dictionary, not a set , to create a set, you should use the builtin function set() .
The if condition - if str2 in word==False: , would never amount to True because of operator chaining, it would be converted to - if str2 in word and word==False , example showing this behavior -
>>> 'a' in 'abcd'==False
False
>>> 'a' in 'abcd'==True
False
In line - for (n,m) in list(enumerate(strlist)) - You do not need to convert the return of enumerate() function to list, you can just iterate over its return value (which is an iterator directly)
Sets do not have any sense of order, when you do - zip(word,index) - there is no guarantee that the elements are zipped in the correct order you want (since they do not have any sense of order at all).
Do not use str as a variable name.
Given this, you are better off directly creating the dictionary from the start , rather than sets.
Code -
def mii(strlist):
word={}
for i, s in enumerate(strlist):
for s2 in s.split():
word.setdefault(s2,set()).add(i)
return word
Demo -
>>> def mii(strlist):
... word={}
... for i, s in enumerate(strlist):
... for s2 in s.split():
... word.setdefault(s2,set()).add(i)
... return word
...
>>> mii(['hello world','hello','hello cat','hellolot of cats'])
{'cats': {3}, 'world': {0}, 'cat': {2}, 'hello': {0, 1, 2}, 'hellolot': {3}, 'of': {3}}

def mii(strlist):
word_list = {}
for index, str in enumerate(strlist):
for word in str.split():
if word not in word_list.keys():
word_list[word] = [index]
else:
word_list[word].append(index)
return word_list
print mii(['hello world','hello','hello cat','hellolot of cats'])
Output:
{'of': [3], 'cat': [2], 'cats': [3], 'hellolot': [3], 'world': [0], 'hello': [0, 1, 2]}
I think this is what you wanted.

Related

Count occurrence of strings in list of lists

I want to count the number of times a string has occurred in a list which is in another list and store it in a list of dictionary where each dictionary has count of a list.
Ex,
list = [['Sam','John','Alex','Sam','Alex'],['Max','Sam','Max']...]
and I want my list of dictionaries to be like:
count_list = [{'Sam':2,'Alex':2,'John':1}, {'Max':2, 'Sam':1}..]
I am iterating through each list to count number of times each string has occurred and adding each result to dict. But I end up having different result every time and not the correct values.
count_list = []
for l in list :
d = {}
for str in l:
if str not in d:
d[str] = l.count(str)
count_list.append(d)
Any help would be useful.Thanks.
It would be easier to use collections.Counter() here:
>>> from collections import Counter
>>> lst = [["Sam", "John", "Alex", "Sam", "Alex"], ["Max", "Sam", "Max"]]
>>> list(map(Counter, lst))
[Counter({'Sam': 2, 'Alex': 2, 'John': 1}), Counter({'Max': 2, 'Sam': 1})]
You could also use a list comprehension instead of using map() if thats easier to understand:
>>> [Counter(l) for l in lst]
[Counter({'Sam': 2, 'Alex': 2, 'John': 1}), Counter({'Max': 2, 'Sam': 1})]
Note: Counter is a subclass of dict, so you can treat them like normal dictionaries.
You can always cast to dict() if you want to as well:
>>> [dict(Counter(l)) for l in lst]
[{'Sam': 2, 'John': 1, 'Alex': 2}, {'Max': 2, 'Sam': 1}]
You should also not use list as a variable name, since it shadows the builtin function list().
Currently, you are doing the following:
count_list = []
for l in list :
d = {}
for str in l:
if str not in d:
d[str] = l.count(str)
count_list.append(d)
Note that you are appending the dictionary for each string in the sub lists, rather than one dictionary per sub list.
Doing the following should address the issue:
count_list = []
for l in list :
d = {}
for str in l:
if str not in d:
d[str] = l.count(str)
count_list.append(d)

Extract a list of keys by Sorting the dictionary in python

I have my program's output as a python dictionary and i want a list of keys from the dictn:
s = "cool_ice_wifi"
r = ["water_is_cool", "cold_ice_drink", "cool_wifi_speed"]
good_list=s.split("_")
dictn={}
for i in range(len(r)):
split_review=r[i].split("_")
counter=0
for good_word in good_list:
if good_word in split_review:
counter=counter+1
d1={i:counter}
dictn.update(d1)
print(dictn)
The conditions on which we should get the keys:
The keys with the same values will have the index copied as it is in a dummy list.
The keys with highest values will come first and then the lowest in the dummy list
Dictn={0: 1, 1: 1, 2: 2}
Expected output = [2,0,1]
You can use a list comp:
[key for key in sorted(dictn, key=dictn.get, reverse=True)]
In Python3 it is now possible to use the sorted method, as described here, to sort the dictionary in any way you choose.
Check out the documentation, but in the simplest case you can .get the dictionary's values, while for more complex operations, you'd define a key function yourself.
Dictionaries in Python3 are now insertion-ordered, so one other way to do things is to sort at the moment of dictionary creation, or you could use an OrderedDict.
Here's an example of the first option in action, which I think is the easiest
>>> a = {}
>>> a[0] = 1
>>> a[1] = 1
>>> a[2] = 2
>>> print(a)
{0: 1, 1: 1, 2: 2}
>>>
>>> [(k) for k in sorted(a, key=a.get, reverse=True)]
[2, 0, 1]

Getting Lists of Summed Nested Dictionary Values

I am trying to write a part of program where the user inputs a Target Word (targetWord = input()), assigns a nested Dictionary with the key being the same as the input word.
For example:
mainDict = {
'x': {'one': 1, 'blue': 1, 'green' :1},
'y': {'red': 1, 'blue': 2, 'two': 1},
'z': {'one': 1, 'green': 1, 'red': 1}
}
where all the values in the nested dictionary are assigned integers.
The user may input 'x', to which the program will assign:
targetDict = mainDict['x']
The program should then allow the user to input words again, but this time every single word from input is appended to a lookup list, for example user inputs 'y', then 'z':
lookup = ['y', 'z']
Then the program should run through the nested dictionary and for every value with the corresponding key as in the targetDict, append just values to a new nested list and add whatever value the nested Dictionary values are. So the output of this section should be:
targetOutput = [[2], [1, 1]]
because in nested dict 'y', only 'blue' was a common key, to which its value 2 was put in a list, then appended onto targetOutput. The same case with dict 'z', where the keys 'one' and 'green' were present in both 'x' and 'z', putting their values, 1 and 1 into a nested list.
Here is a representation of the dysfunctional code I have for:
targetOutput = []
targetDict = mainDict[targetWord]
for tkey in targetDict:
tt = []
for word in lookup:
for wkey in primaryDict[word]:
if tkey == wkey:
tt.append(targetDict[tkey])
tl.append(sum(tt))
print((pl))
The sum function at the end is because my actually final output should be the sum of the values in the nested list, akin to:
tl = [[2], [2]]
I am also trying to make the reverse happen, where in another list for every key in the lookup, it returns a nested list containing the sum of every value the targetWord dictionary also has a key for, like:
ll = [[2], [2]]
My question is, how do I fix my code so that it outputs the 2 above lists? I'm quite new with dictionaries.
The .keys() method on a dictionary gives you a dictionary view, which can act like a set. This means you can take the intersection between the key views of two dictionaries! You want the intersection between the inital targetDict and the dictionaries named in lookup:
for word in lookup:
other_dict = mainDict[word]
common_keys = targetDict.keys() & other_dict
targetOutput.append([other_dict[common] for common in common_keys])
The targetDict.keys() & other_dict expression produces the intersection here:
>>> mainDict = {
... 'x': {'one': 1, 'blue': 1, 'green' :1},
... 'y': {'red': 1, 'blue': 2, 'two': 1},
... 'z': {'one': 1, 'green': 1, 'red': 1}
... }
>>> targetDict = mainDict['x']
>>> targetDict.keys() & mainDict['y']
{'blue'}
>>> targetDict.keys() & mainDict['z']
{'green', 'one'}
The [other_dict[common] for common in common_keys] list comprehension takes those keys and looks up the values for them from the other dictionary.
If you want to sum the values, just pass the same sequence of values to the sum() function:
for word in lookup:
other_dict = mainDict[word]
common_keys = targetDict.keys() & other_dict
summed_values = sum(other_dict[common] for common in common_keys)
targetOutput.append(summed_values)
There is no point in wrapping the summed values in another list there as there is only ever going to be a single sum. The above gives you a targetOutput list with [2, 2], not [[2], [2]].

List comprehension vs set comprehension

I have the following program. I am trying to understand list comprehension and set comprehension:
mylist = [i for i in range(1,10)]
print(mylist)
clist = []
for i in mylist:
if i % 2 == 0:
clist.append(i)
clist2 = [x for x in mylist if (x%2 == 0)]
print('clist {} clist2 {}'.format(clist,clist2))
#set comprehension
word_list = ['apple','banana','mango','cucumber','doll']
myset = set()
for word in word_list:
myset.add(word[0])
myset2 = {word[0] for word in word_list}
print('myset {} myset2 {}'.format(myset,myset2))
My question is why the curly braces for myset2 = {word[0] for word in word_list}.
I haven't come across sets in detail before.
Curly braces are used for both dictionary and set comprehensions. Which one is created depends on whether you supply the associated value or not, like following (3.4):
>>> a={x for x in range(3)}
>>> a
{0, 1, 2}
>>> type(a)
<class 'set'>
>>> a={x: x for x in range(3)}
>>> a
{0: 0, 1: 1, 2: 2}
>>> type(a)
<class 'dict'>
Set is an unordered, mutable collection of unrepeated elements.
In python you can use set() to build a set, for example:
set>>> set([1,1,2,3,3])
set([1, 2, 3])
>>> set([3,3,2,5,5])
set([2, 3, 5])
Or use a set comprehension, like a list comprehension but with curly braces:
>>> {x for x in [1,1,5,5,3,3]}
set([1, 3, 5])

Python - tuple unpacking in dict comprehension

I'm trying to write a function that turns strings of the form 'A=5, b=7' into a dict {'A': 5, 'b': 7}. The following code snippets are what happen inside the main for loop - they turn a single part of the string into a single dict element.
This is fine:
s = 'A=5'
name, value = s.split('=')
d = {name: int(value)}
This is not:
s = 'A=5'
d = {name: int(value) for name, value in s.split('=')}
ValueError: need more than 1 value to unpack
Why can't I unpack the tuple when it's in a dict comprehension? If I get this working then I can easily make the whole function into a single compact dict comprehension.
In your code, s.split('=') will return the list: ['A', '5']. When iterating over that list, a single string gets returned each time (the first time it is 'A', the second time it is '5') so you can't unpack that single string into 2 variables.
You could try: for name,value in [s.split('=')]
More likely, you have an iterable of strings that you want to split -- then your dict comprehension becomes simple (2 lines):
splitstrs = (s.split('=') for s in list_of_strings)
d = {name: int(value) for name,value in splitstrs }
Of course, if you're obsessed with 1-liners, you can combine it, but I wouldn't.
Sure you could do this:
>>> s = 'A=5, b=7'
>>> {k: int(v) for k, v in (item.split('=') for item in s.split(','))}
{'A': 5, ' b': 7}
But in this case I would just use this more imperative code:
>>> d = {}
>>> for item in s.split(','):
k, v = item.split('=')
d[k] = int(v)
>>> d
{'A': 5, ' b': 7}
Some people tend to believe you'll go to hell for using eval, but...
s = 'A=5, b=7'
eval('dict(%s)' % s)
Or better, to be safe (thanks to mgilson for pointing it out):
s = 'A=5, b=7'
eval('dict(%s)' % s, {'__builtins__': None, 'dict': dict})
See mgilson answer to why the error is happening. To achieve what you want, you could use:
d = {name: int(value) for name,value in (x.split('=',1) for x in s.split(','))}
To account for spaces, use .strip() as needed (ex.: x.strip().split('=',1)).
How about this code:
a="A=5, b=9"
b=dict((x, int(y)) for x, y in re.findall("([a-zA-Z]+)=(\d+)", a))
print b
Output:
{'A': 5, 'b': 9}
This version will work with other forms of input as well, for example
a="A=5 b=9 blabla: yyy=100"
will give you
{'A': 5, 'b': 9, 'yyy': 100}
>>> strs='A=5, b=7'
>>> {x.split('=')[0].strip():int(x.split('=')[1]) for x in strs.split(",")}
{'A': 5, 'b': 7}
for readability you should use normal for-in loop instead of comprehensions.
strs='A=5, b=7'
dic={}
for x in strs.split(','):
name,val=x.split('=')
dic[name.strip()]=int(val)
How about this?
>>> s
'a=5, b=3, c=4'
>>> {z.split('=')[0].strip(): int(z.split('=')[1]) for z in s.split(',')}
{'a': 5, 'c': 4, 'b': 3}
Since Python 3.8, you can use walrus operator (:=) for this kind of operation. It allows to assign variables in the middle of expressions (in this case, assign the list created by .split('=') to kv).
s = 'A=5, b=7'
{(kv := item.split('='))[0]: int(kv[1]) for item in s.split(', ')}
# {'A': 5, 'b': 7}
One feature is that it leaks the assigned variable, kv, outside the scope it was defined in. If you want to avoid that, you can use a nested for-loop where the inner loop is over a singleton list (as suggested in mgilson's answer).
{k: int(v) for item in s.split(', ') for k,v in [item.split('=')]}
Since Python 3.9, loops over singleton lists are optimized to be as fast as simple assignments, i.e. y in [expr] is as fast as y = expr.

Categories