'list' object has no attribute 'lower' in Python - python

function anagrams(s1, s2) is a Boolean valued function, which returns true just in case the string s1 contains the same letters as string s2 but in a different order. The function should be case insensitive --- in other words it should return the same value if any letters in either s1 or s2 are changed from upper to lower case or from lower to upper case. You may assume that the input strings contain only letters.
The function find_all_anagrams(string) takes a string as input and returns a list of all words in the file english_words.txt that are anagrams of the input string.
the function should return a list [word1, ..., wordN] such that each word in this list is a word in the dictionary file such that the value function anagrams(string, word) are True
def anagrams( string1, string2 ):
str_1 = string1.lower()
str_2 = string2.lower()
if str_1 == str_2:
return False
else:
list_1 = list( str_1 )
list_1.sort()
list_2 = list( str_2 )
list_2.sort()
return list_1 == list_2
def find_all_anagrams( string ):
with open("english_words.txt") as f:
word_list = []
for line in f.readlines():
word_list.append(line.strip())
list1 = [i.split() for i in word_list]
for j in list1:
if anagrams( string, j ) == True:
return list1
else:
return []
ERROR kept saying this: AttributeError: 'list' object has no attribute 'lower'
for example,word_list contains:
['pyruvates', 'python', 'pythoness', 'pythonesses', 'pythonic', 'pythons', 'pyuria', 'pyurias', 'pyx', 'pyxes']
Expected output below
Part of the txt file shown on the right:
Update:
I think I just solved it,here are my codes:
def find_all_anagrams( string ):
list1 = []
with open("english_words.txt") as f:
word_list = []
for line in f.readlines():
word_list.append(line.strip())
for i in word_list:
if anagrams( string, i ):
list1.append(i)
return list1

You are using split() function at this part:
list1 = [i.split() for i in word_list]
Let's see what the documentation tells us about that function:
str.split(sep=None, maxsplit=-1)
Return a list of the words in the
string, using sep as the delimiter string. If maxsplit is given, at
most maxsplit splits are done (thus, the list will have at most
maxsplit+1 elements). If maxsplit is not specified or -1, then there
is no limit on the number of splits (all possible splits are made).
It returns a list, and you added that list to your own list. I can see that word_list is meant to hold lines of words. Let's assume that word_list looks like this:
word_list = ["hello darkness my", "old friend I've", "come to see you", "again"]
What happens after list1 = [i.split() for i in word_list]?
list1 = [i.split() for i in word_list]
print(list1)
Output:
[['hello', 'darkness', 'my'], ['old', 'friend', "I've"], ['come', 'to', 'see', 'you'], ['again']]
As you can see, elements are individual lists. At this part of your code:
for j in list1:
if anagrams( string, j ) == True:
return list1
else:
return []
j is a list, therefore here:
def anagrams( string1, string2 ):
str_1 = string1.lower()
str_2 = string2.lower()
str_2 = string2.lower() is trying to call lower method on a list, which isn't a valid method for a list object, and that's why Python is complaining.
List Comprehension might look "cool" but often using simple loops benefits your code's readability, and in some cases might even avoid mistakes like this one. Here is my alternative:
list1 = []
for i in word_list:
for word in i.split():
list1.append(word)
see the output:
print(list1)
['hello', 'darkness', 'my', 'old', 'friend', "I've", 'come', 'to', 'see', 'you', 'again']
Single words as you intended.

As indicated by the error message, list does not have an attribute .lower
I guess what you meant do to is access a string within the list with a .lower attribute.
For example:
mylist[index].lower()
where index corresponds to the string position within the list.

Related

Strings searches in a list, if the list contains a string with a space

I'd like to identify a word in a list, however one of the strings has a space in-between and is not recognized. My code:
res = [word for word in somestring if word not in myList]
myList = ["first", "second", "the third"]
So when
somestring = "test the third"
is parsed then res="test the third" (should be "test").
How can I overcome strings searches in a list, if the list contains a string with a space?
One way is you can use split().
myList = ["first", "second", "the third"]
somestring = "test the third"
n=[x.split() for x in myList]
#[['first'], ['second'], ['the', 'third']]
You can flatten this by:
m=[item for sublist in n for item in sublist]
#['first', 'second', 'the', 'third']
Similarly, you can split() somestring
s=somestring.split()
#['test', 'the', 'third']
Finally:
for x in s:
if x not in m:
print(x)
#test
You can also get the result in one line; but it is not very readable:
[x for x in somestring.split() if x not in (item for sublist in (x.split() for x in myList) for item in sublist)]
#['test']
Use myLst as a list of patterns for regex substitution:
import re
myList = ["first", "second", "the third"]
somestring = "test the third"
res = re.sub(fr'({"|".join(myList)})', '', somestring).strip()
print(res)
test

How to remove a word from a list with a specific character in a specific index position

this is what I have so far:
wlist = [word for word in wlist if not any(map(lambda x: x in word, 'c'))]
this code works, however in its current state it will remove all strings from wlist which contain 'c'. I would like to be able to specify an index position. For example if
wlist = ['snake', 'cat', 'shock']
wlist = [word for word in wlist if not any(map(lambda x: x in word, 'c'))]
and I select index position 3 than only 'shock' will be removed since 'shock' is the only string with c in index 3. the current code will remove both 'cat' and 'shock'. I have no idea how to integrate this, I would appreciate any help, thanks.
Simply use slicing:
out = [w for w in wlist if w[3:4] != 'c']
Output: ['snake', 'cat']
Probably you should use regular expressions. How ever I don`t know them )), so just iterates through list of words.
for i in wlist:
try:
if i[3] == 'c':
wlist.remove(i)
except IndexError:
continue
You should check only the 3rd character in your selected string. If you use the [<selected_char>:<selected_char>+1] list slicing then only the selected character will be checked. More about slicing: https://stackoverflow.com/a/509295/11502612
Example code:
checking_char_index = 3
wlist = ["snake", "cat", "shock", "very_long_string_with_c_char", "x", "c"]
out = [word for word in wlist if word[checking_char_index : checking_char_index + 1] != "c"]
print(out)
As you can see, I have extended your list with some corner-case strings and it works as expected as you can see below.
Output:
>>> python3 test.py
['snake', 'cat', 'very_long_string_with_c_char', 'x', 'c']

Iterating through list and using remove() doesn't produce desired result

I’m a programming neophyte and would like some assistance in understanding why the following algorithm is behaving in a particular manner.
My objective is for the function to read in a text file containing words (can be capitalized), strip the whitespace, split the items into separate lines, convert all capital first characters to lowercase, remove all single characters (e.g., “a”, “b”, “c”, etc.), and add the resulting words to a list. All words are to be a separate item in the list for further processing.
Input file:
A text file (‘sample.txt’) contains the following data - “a apple b Banana c cherry”
Desired output:
[‘apple’, ‘banana’, ‘cherry’]
In my initial attempt I tried to iterate through the list of words to test if their length was equal to 1. If so, the word was to be removed from the list, with the other words remaining in the list. This resulted in the following, non-desired output: [None, None, None]
filename = ‘sample.txt’
with open(filename) as input_file:
word_list = input_file.read().strip().split(' ')
word_list = [word.lower() for word in word_list]
word_list = [word_list.remove(word) for word in word_list if len(word) == 1]
print(word_list)
Produced non-desired output = [None, None, None]
My next attempt was to instead iterate through the list for words to test if their length was greater than 1. If so, the word was to be added to the list (leaving the single characters behind). The desired output was achieved using this method.
filename = ‘sample.txt’
with open(filename) as input_file:
word_list = input_file.read().strip().split(' ')
word_list = [word.lower() for word in word_list]
word_list = [word for word in word_list if len(word) > 1]
print(word_list)
Produced desired Output = [‘apple’, ‘banana’, ‘cherry’]
My questions are:
Why didn’t the initial code produce the desired result when it seemed to be the most logical and most efficient?
What is the best ‘Pythonic’ way to achieve the desired result?
The reason you got the output you got is
You're removing items from the list as you're looping through it
You are trying to use the output of list.remove (which just modifies the list and returns None)
Your last list comprehension (word_list = [word_list.remove(word) for word in word_list if len(word) == 1]) is essentially equivalent to this:
new_word_list = []
for word in word_list:
if len(word) == 1:
new_word_list.append(word_list.remove(word))
word_list = new_word_list
And as you loop through it this happens:
# word_list == ['a', 'apple', 'b', 'banana', 'c', 'cherry']
# new_word_list == []
word = word_list[0] # word == 'a'
new_word_list.append(word_list.remove(word))
# word_list == ['apple', 'b', 'banana', 'c', 'cherry']
# new_word_list == [None]
word = word_list[1] # word == 'b'
new_word_list.append(word_list.remove(word))
# word_list == ['apple', 'banana', 'c', 'cherry']
# new_word_list == [None, None]
word = word_list[2] # word == 'c'
new_word_list.append(word_list.remove(word))
# word_list == ['apple', 'banana', 'cherry']
# new_word_list == [None, None, None]
word_list = new_word_list
# word_list == [None, None, None]
The best 'Pythonic' way to do this (in my opinion) would be:
with open('sample.txt') as input_file:
file_content = input_file.read()
word_list = []
for word in file_content.strip().split(' '):
if len(word) == 1:
continue
word_list.append(word.lower())
print(word_list)
In your first approach, you are storing the result of word_list.remove(word) in the list which is None. Bcz list.remove() method return nothing but performing action on a given list.
Your second approach is the pythonic way to achieve your goal.
The second attempt is the most pythonic. The first one can still be achieved with the following:
filename = 'sample.txt'
with open(filename) as input_file:
word_list = input_file.read().strip().split(' ')
word_list = [word.lower() for word in word_list]
for word in word_list:
if len(word) == 1:
word_list.remove(word)
print(word_list)
Why didn’t the initial code produce the desired result when it seemed
to be the most logical and most efficient?
It's advised to never alter a list while iterating over it. This is because it is iterating over a view of the initial list and that view will differ from the original.
What is the best ‘Pythonic’ way to achieve the desired result?
Your second attempt. But I'd use a better naming convention and your comprehensions can be combined as you're only making them lowercase in the first one:
word_list = input_file.read().strip().split(' ')
filtered_word_list = [word.lower() for word in word_list if len(word) > 1]

how could i get two next words of specific word from string?

I have a list of specific words
['to', 'with', 'in', 'for']
I want to make a function, which takes a sentence and if there is a word form my list, it should select the next two words after it and put them joined to the list(i need it for a part of my sentence generator). For example:
sentence = 'In the morning I went to the store and then to the restaurant'
I want to get
['tothe', 'tostore', 'tothe', 'torestaurant']
I wrote this code:
preps = ['to', 'with', 'in', 'for']
def nextofnext_words_ofnegs(sentence):
list_of_words = sentence.split()
next_word = []
for i in list_of_words:
for j in preps:
if i == j:
next_word.append(j + list_of_words[list_of_words.index(i) + 1])
next_word.append(j + list_of_words[list_of_words.index(i) + 2])
return next_word
However i get this:
['tothe', 'tostore', 'tothe', 'tostore']
Instead of this:
['tothe', 'tostore', 'tothe', 'torestaurant']
This should work to give you what you want. Note that you can use the "in" operator in Python to check if the word exists in your string list, there is no need to loop the list here in this case. Also as mentioned above, using of .index is insufficient here, you can use enumerate to get the index as well as the item in the list.
preps = ['to', 'with', 'in', 'for']
def nextofnext_words_ofnegs(sentence):
list_of_words = sentence.split()
next_word = []
for idx, word in enumerate(list_of_words):
if word in preps:
next_word.append(word + list_of_words[idx + 1])
next_word.append(word + list_of_words[idx + 2])
return next_word

Comparing strings in a list and appending those that have the same first and last character to a new list

I'm in an Intro to Python class and was given this assignment:
Given a list of strings, return a new list containing all the strings from the original list that begin and end with the same character. Matching is not case-sensitive, meaning 'a' should match with 'A'. Do not alter the original list in any way.
I was running into problems with slicing and comparing the strings because the possible lists given include '' (empty string). I'm pretty stumped and any help would be appreciated.
def first_last(strings):
match=[]
x=''
count=0
while count<len(strings):
if x[0] == x[-1]:
match.append(x)
x+=x
count+=1
So, when given:
['aba', 'dcn', 'z', 'zz', '']
or
['121', 'NbA', '898', '']
I get this:
string index out of range
When I should be seeing:
['aba', 'z', 'zz']
and
['121', '898']
Your list contains an empty string (''). Thus, you will have to check for the length of each element that you are currently iterating over. Also, it does not seem that you use x:
def first_last(strings):
match=[]
count=0
while count<len(strings):
if strings[count]:
if strings[count][0].lower() == strings[count][-1].lower():
match.append(strings[count])
count += 1
return match
Note, however, that you can also use list comprehension:
s = ['aba', 'dcn', 'z', 'zz', '']
final_strings = [i for i in s if i and i[0].lower() == i[-1].lower()]
def first_last(strings):
match=[]
for x in strings:
if x is '' continue;
if x.lower()[0] == x.lower()[-1]:
match.append(x)
return match
Test if the list element is not None first:
def first_last(strings):
match = []
for element in strings:
if element and element[0].lower() == element[-1].lower():
match.append(element)
return match
or with list comp:
match = [element for element in strings if element and element[0].lower() == element[-1].lower()]

Categories