Conceputalize breaking down a list comprehension of enumerate - python

I'm starting out here and love list comprehension. I found this following line of code for a practice problem of capitalizing every other letter in a string and it makes sense to me. The problem I'm hoping to get help with is figuring out how to write this code normally (for loop, if statements, etc. Beginners stuff) without list comprehension.
Here's the code that I started with that I want to break down:
s = input('Please enter a string: ')
answer = ''.join(char.upper() if idx % 2 else char.lower() for idx, char in enumerate(s))
print(answer)
And here is what I thought was a proper code to reproduce what this one above was doing:
s = input('Please enter a string: ')
for idx, char in enumerate(s):
if idx % 2:
s = char.upper()
else:
s = char.lower()
answer = ''.join(s)
print(answer)
If I were to type Hello, I should get hElLo, but instead I get o
I'd appreciate any advice or tips on how to proceed here. Thanks!

Technically you're using a generator expression, and not a list comprehension. But the result is similar in this case.
You're reading input into s, but then you're reassigning s in each iteration, and then you're joining s.
You need to have a different variable. For the input and for the capitalized list. What a generator expression does is to return one value at a time, instead of building the whole capitalized list at once. But here you will need to declare it.
Also, it doesn't make sense to assign answer in each iteration, the join is the last thing you need to do, once your capitalized list is prepared.
This should work:
toCapitalize = input('Please enter a string: ')
capitalizedList = []
for idx, char in enumerate(toCapitalize):
if idx % 2:
capitalizedList.append(char.upper())
else:
capitalizedList.append(char.lower())
answer = ''.join(capitalizedList)
print(answer)
In case this helps, I've tried to reflect what line goes with what part of the generator expression below:
for idx, char in enumerate(toCapitalize): # for idx, char in enumerate(s)
if idx % 2: capitalizedList.append(char.upper()) # char.upper() if idx % 2
else: capitalizedList.append(char.lower()) # else char.lower()
answer = ''.join(capitalizedList) # answer = ''.join()
Again, the capitalizedList variable is implicit in the generator expressions or list comprehensions.
Generator expressions
To understand generator expressions look at this code:
capitalize = 'hello'
generator = (char.upper() if idx % 2 else char.lower() for idx, char in enumerate(capitalize))
print(next(generator)) # h
print(next(generator)) # E
print(next(generator)) # l
print(next(generator)) # L
print(next(generator)) # o
print(next(generator)) # raises a StopIteration exception, we've reached the end.
Each call to next() calculates the result of the next iteration on the fly. Which is more memory efficient than building a whole list at once when you have big lists. In your case, the call to join() consumes all the generator, and joins the values returned.
As a list comprehension
Your code as a list comprehension would be:
s = input('Please enter a string: ')
answer = ''.join([ char.upper() if idx % 2 else char.lower() for idx, char in enumerate(s) ])
print(answer)
As I explained above, the difference is that here we're building a whole list at once, the generator is only returning one value at a time.
Your code as a generator
And finally, to be technically correct your code would be equivalent to this:
def generator(s):
for idx, char in enumerate(s):
if idx % 2:
yield char.upper()
else:
yield char.lower()
answer = ''.join(generator(s))
print(answer)
That's how you build a generator in Python.

Related

How to use insert() in a for loop without making it a endless loop?

I'm trying to make a project in python so that whenever the program encounters a capital letter it makes a white space. So for example "helloThisIsMyProject" > hello This is My Project.
def project(w):
lst = []
for i in w:
lst.append(i)
for letter in lst:
if letter.isupper():
index = lst.index(letter)
lst.insert(index, " ")
continue
return "".join(lst)
print(project("helloThisIsMyProject"))
I'm having a problem with insert() and the for loop because the for loop is endlessly looping over the "T" from "This". I tried using continue to skip the letter T but it didn't fix my problem.
Those 1st three lines are simply
lst = list(w)
But you would find this more convenient:
lst = []
for letter in w:
if letter.isupper():
lst.append(" ")
lst.append(letter)
That is, rather than producing a list and going back to fix it up,
you could have it correct from the outset.
You can conditionally output a SPACE before you output the letter.
If you prefer to do it with a list comprehension,
you might enlist the aid of a helper:
def adjust(s: str) -> str:
"""Returns the input string, possibly whitespace adjusted."""
return " " + s if s.isupper() else s
lst = [adjust(letter)
for letter in w]
or more compactly:
lst = list(map(adjust, w))
You could use a join with a list comprehension that adds the space to individual characters:
def spaceCaps(w):
return "".join(" "*c.isupper()+c for c in w)
spaceCaps("helloThisIsMyProject")
'hello This Is My Project'
You could also use the sub() function from the regular expression module:
import re
def spaceCaps(w):
return re.sub("([A-Z])",r" \1",w)

To Remove consecutive vowels from string

Here I want to remove any vowels which repeat consecutively. But it shows error as "list out of index".
So I tried break if last element in list is reached, but still won't work.
Here is the code I tried:-
a=[]
b=str(input("enter the string"))
a=b.split(',')
c=['a','e','i','o','u']
for i in c:
for j in range(0,len(a)):
if (a[j+1] == a[len(a)]) is True:
break;
elif ((a[j] == a[j+1]) & (a[j+1] == i)) is True:
del[j]
e.join(a)
print(e)
Please show me how to solve this problem, or any other problem if in there.
How about maintaining a stack for consecutive vowels? whenever you see non-vowel string re-initialize the stack list and when you see vowels but are not consecutive you just add to the final list
stack=[]
new_list=[]
vowel=['a','i','o','u','e']
for i in your_string: # replace your string with actual string
if i not in vowel:
if len(stack) == 1:
new_list.append(stack[0])
new_list.append(i)
stack = []
else:
stack.append(i)
if len(stack) == 1:
new_list.append(stack[0])
You get a list out of index error because you are indexing at a value not in a
if (a[j+1] == a[len(a)]) is True:
a[len(a)] does not exist, arrays are zero indexed so they start from 0. An array with 5 items has index 0,1,2,3,4
the line should be:
if (a[j+1] == a[len(a) - 1]) is True:
Also 'is True' is redundant, so further refined:
if a[j+1] == a[len(a) - 1]:
Also is del[j] an error? should it be del a[j]?
If this is the case, the program will run into further errors as you are iterating over the entire Original size of the array but deleting values during this iteration, so it will look for items that no longer exist.
First detail is this: if (a[j+1] == a[len(a)]) is True: from what I understood this is to break the code when it is necessary. But that is completely unnecessary. Instead you should fix the number of iterations in the beginning, which should be for j in range(0,len(a)-1):
Another problem is that you aren't really iterating through the letters, just each comma separated phrases. If you put "Hello, World" you aren't checking the letters, you check "Hello" and " World". You might as well remove b and let a just be the raw input.
Since a would then be a string, to erase the i-th letter of a string you can use the function below.
def remove_jth(word, j):
word = word[:j] + word[j+1:]
Finally since you are using range, it will build a "list" that has the size len(a) when you start the for, but as you remove vowels, the length of a gets shorter and the range you had at the beginning will be to much. If you fix all of these things you should have it right
You can do it this way:
a=[]
b=str("a,a,a,b,e,e,e,c,x,d")
a=b.split(',')
c=['a','e','i','o','u']
j = 0
for i in c:
while j < len(a)-1:
if a[j] == a[j+1] and a[j] == i:
del a[j]
else:
j = j+1
j=0
Use a while loop to check through the list and delete consecutive duplicates. Then reset the the while loop back to zero and run through the list again.
Using a for loop will not work,if you are deleting items in the list that is being used to iterate over.
b=str("a,a,a,b,e,e,e,c,x,d") was just used to test the code.
To print the results you can just do print(a) it will print the list out.
I used this Stack Overflow post as reference.
I edited it to suit you:
result=original.replace('a','').replace('e',''),replace('i','').replace('o','').replace('u','')
original is your input string and result is your output string.
t=input()
st=""
vow=['a','e','i','o','u']
st+=t[0]
for i in range(1,len(t)):
if(t[i].lower() in vow):
if(t[i-1].lower() not in vow and i-1>=0):
st+=t[i]
else:
st+=t[i]
print(st)

How to change case for alternative letters of a string

I would like to convert every other letter of a string into uppercase. For example provided the input is 'ahdjeryu', the result should be 'AhDjErYu'.
I was trying this:
def mycode(letters):
myword = letters.split()
for i in letters:
if i%2 == 0:
return i.upper()
else:
return i.lower()
print(mycode('ahdjeryu'))
The error thrown as:
if i%2==0:
TypeError: not all arguments converted during string formatting
Several issues with your code:
You only need to use str.split to remove whitespace. Here, not necessary.
To extract the index of a letter as well as the letter, use enumerate.
return will return just one letter. You can instead yield letters and then use str.join on the generator.
Here's a working example:
def mycode(letters):
for idx, i in enumerate(letters):
if idx % 2 == 0:
yield i.upper()
else:
yield i.lower()
print(''.join(mycode('ahdjeryu')))
AhDjErYu
The above logic can be equivalently implemented via a generator comprehension:
res = ''.join(i.upper() if idx % 2 == 0 else i.lower() for idx, i in enumerate(letters))
Your i is a string of one character, not an index. So you can't do i%2. Use enumerate to get both the index and the value.
alternating = ''.join(letter.upper() if index%2==0 else letter.lower() for index, letter in enumerate(text)
Yes, it error, cause i now is letter not a number, so it can't mod for 2.
Im not sure syntax about looping by for in Python but may you can try:
cnt = 0
for i in letters:
if cnt%2 == 0:
return i.upper()
else:
return i.lower()
cnt++
Hope it can help you!
Fix me if I have any wrong. Thanks
#Abhi, this is inline with your original logic. You need r to get the index of the letters in the string.
def mycode(letters):
for ch in letters:
r=letters.index(ch)
if r%2 == 0:
letters=letters.replace(ch,letters[r].upper())
else:
letters=letters.replace(ch,letters[r].lower())
return letters
print(mycode('ahdjeryu'))

Reference next item in list: python

I'm making a variation of Codecademy's pyglatin.py to make a translator that accepts and translates multiple words. However, I'm having trouble translating more than one word. I've been able to transfer the raw input into a list and translate the first, but I do not know how to reference the next item in the list. Any help would be greatly appreciated.
def piglatin1():
pig = 'ay'
original = raw_input('Enter a phrase:').split(' ')
L = list(original)
print L
i = iter(L)
item = i.next()
for item in L:
if len(item) > 0 and item.isalpha():
word = item.lower()
first = word
if first == "a" or first == "e" or first == "i" or first == "o" or first =="u":
new_word = word + pig
print new_word
else:
new_word = word[1:] + word[0:1] + pig
# first word translated
L = []
M = L[:]
L.append(new_word)
print L # secondary list created.
again = raw_input('Translate again? Y/N')
print again
if len(again) > 0 and again.isalpha():
second_word = again.lower()
if second_word == "y":
return piglatin()
else:
print "Okay Dokey!"
else:
print 'Letters only please!'
return piglatin1()
I was working on this problem recently as well and came up with the following solution (rather than use range, use enumerate to get the index).
for index, item in enumerate(L):
next = index + 1
if next < len(L):
print index, item, next
This example shows how to access the current index, the current item, and then the next item in the list (if it exists in the bounds of the list).
Here are a few things to note that might help.
The lines i = iter(L) and item = i.next() are unnecessary. They have no effect in this method because you are redefining item immediately afterwards in the line for item in L. Go ahead and comment out those two lines to see if it makes any changes in your output.
The looping construct for item in L will go once over every item in the list. Whatever code you write within this loop will be executed once for each item in the list. The variable item is your handle to the list element of an iteration.
If, during any iteration, you really do want to access the "next" element in the list as well, then consider using a looping construct such as for i in range(0,len(L)). Then L[i] will be the current item and L[i+1] will you give the subsequent item.
There are some slight issues with the code but I think there is one main reason why it will not repeat.
In order to process the entire string the
again = raw_input('Translate again? Y/N')
and it's succeeding lines should be brought outside the for statement.
Also you appear to be setting L to an empty string inside the loop:
L = []
The following is a modified version of your code which will loop through the entire sentence and then ask for another one.
def piglatin():
pig = 'ay'
while True:
L = raw_input('Enter a phrase:').split(' ')
M = []
for item in L:
if len(item) > 0 and item.isalpha():
word = item.lower()
first = word
if first == "a" or first == "e" or first == "i" or first == "o" or first =="u":
new_word = word + pig
print new_word
else:
new_word = word[1:] + word[0:1] + pig
M.append(new_word)
else:
print 'Letters only please!'
print M # secondary list created.
again = raw_input('Translate again? Y/N')
print again
if len(again) > 0 and again.isalpha():
second_word = again.lower()
if second_word == "n":
print "Okay Dokey!"
break
Changes made:
You don't need to cast the return of the split to a list. The split
return type is a list.
It isn't necessary to make an iterator, the for loop will do this for you.
I removed the function as the return type. I'm assuming you were attempting some form of recursion but it isn't strictly necessary.
Hope this helps.
Step by step:
If you set variable original in this way:
original = raw_input('Enter a phrase:').split()
it will be already a list, so need to additional assignment.
What is the purpose of these lines?
i = iter(L)
item = i.next()
In a loop, you assign variable to the word, when it is actually only the first letter of the word, so it’ll be better like this: first = word[0]
Then if you want to check if first is a vowel, you can just do:
if first in 'aeuoiy'
Answer to your actual question: do not assign L to an empty list!
If you want to repeat the action of a function, you can just call it again, no need to rewrite the code.

Count vowels from raw input

I have a homework question which asks to read a string through raw input and count how many vowels are in the string. This is what I have so far but I have encountered a problem:
def vowels():
vowels = ["a","e","i","o","u"]
count = 0
string = raw_input ("Enter a string: ")
for i in range(0, len(string)):
if string[i] == vowels[i]:
count = count+1
print count
vowels()
It counts the vowels fine, but due to if string[i] == vowels[i]:, it will only count one vowel once as i keeps increasing in the range. How can I change this code to check the inputted string for vowels without encountering this problem?
in operator
You probably want to use the in operator instead of the == operator - the in operator lets you check to see if a particular item is in a sequence/set.
1 in [1,2,3] # True
1 in [2,3,4] # False
'a' in ['a','e','i','o','u'] # True
'a' in 'aeiou' # Also True
Some other comments:
Sets
The in operator is most efficient when used with a set, which is a data type specifically designed to be quick for "is item X part of this set of items" kind of operations.*
vowels = set(['a','e','i','o','u'])
*dicts are also efficient with in, which checks to see if a key exists in the dict.
Iterating on strings
A string is a sequence type in Python, which means that you don't need to go to all of the effort of getting the length and then using indices - you can just iterate over the string and you'll get each character in turn:
E.g.:
for character in my_string:
if character in vowels:
# ...
Initializing a set with a string
Above, you may have noticed that creating a set with pre-set values (at least in Python 2.x) involves using a list. This is because the set() type constructor takes a sequence of items. You may also notice that in the previous section, I mentioned that strings are sequences in Python - sequences of characters.
What this means is that if you want a set of characters, you can actually just pass a string of those characters to the set() constructor - you don't need to have a list one single-character strings. In other words, the following two lines are equivalent:
set_from_string = set('aeiou')
set_from_list = set(['a','e','i','o','u'])
Neat, huh? :) Do note, however, that this can also bite you if you're trying to make a set of strings, rather than a set of characters. For instance, the following two lines are not the same:
set_with_one_string = set(['cat'])
set_with_three_characters = set('cat')
The former is a set with one element:
'cat' in set_with_one_string # True
'c' in set_with_one_string # False
Whereas the latter is a set with three elements (each one a character):
'c' in set_with_three_characters` # True
'cat' in set_with_three_characters # False
Case sensitivity
Comparing characters is case sensitive. 'a' == 'A' is False, as is 'A' in 'aeiou'. To get around this, you can transform your input to match the case of what you're comparing against:
lowercase_string = input_string.lower()
You can simplify this code:
def vowels():
vowels = 'aeiou'
count = 0
string = raw_input ("Enter a string: ")
for i in string:
if i in vowels:
count += 1
print count
Strings are iterable in Python.
for i in range(0, len(string)):
if string[i] == vowels[i]:
This actually has a subtler problem than only counting each vowel once - it actually only tests if the first letter of the string is exactly a, if the second is exactly e and so on.. until you get past the fifth. It will try to test string[5] == vowels[5] - which gives an error.
You don't want to use i to look into vowels, you want a nested loop with a second index that will make sense for vowels - eg,
for i in range(len(string)):
for j in range(len(vowels)):
if string[i] == vowels[j]:
count += 1
This can be simplified further by realising that, in Python, you very rarely want to iterate over the indexes into a sequence - the for loop knows how to iterate over everything that you can do string[0], string[1] and so on, giving:
for s in string:
for v in vowels:
if s == v:
count += 1
The inner loop can be simplified using the in operation on lists - it does exactly the same thing as this code, but it keeps your code's logic at a higher level (what you want to do vs. how to do it):
for s in string:
if s in vowels:
count += 1
Now, it turns out that Python lets do math with booleans (which is what s in vowels gives you) and ints - True behaves as 1, False as 0, so True + True + False is 2. This leads to a one liner using a generator expression and sum:
sum(s in vowels for s in string)
Which reads as 'for every character in string, count how many are in vowels'.
you can use filter for a one liner
print len(filter(lambda ch:ch.lower() in "aeiou","This is a String"))
Here's a more condensed version using sum with a generator:
def vowels():
string = raw_input("Enter a string: ")
print sum(1 for x in string if x.lower() in 'aeiou')
vowels()
Option on a theme
Mystring = "The lazy DOG jumped Over"
Usestring = ""
count=0
for i in Mystring:
if i.lower() in 'aeiou':
count +=1
Usestring +='^'
else:
Usestring +=' '
print (Mystring+'\n'+Usestring)
print ('Vowels =',count)
The lazy DOG jumped Over
^ ^ ^ ^ ^ ^ ^
Vowels = 7

Categories

Resources