Getting index of duplicate letters in a string - string

from graphics import *
import random
def hangman(word):
returnStuff = {'again':0, '1st':1}
alphabet = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']
win = GraphWin("Hangman", 800, 550)
win.setBackground("yellow")
titleText = Text(Point(400,50), 'HANGMAN')
titleText.setSize(24)
titleText.setStyle('bold')
titleText.draw(win)
#Building the hangman base
base = Line(Point(120,350),Point(230,350))
base.draw(win)
stand = Line(Point(175,350),Point(175,150))
stand.draw(win)
stand2 = Line(Point(175,150),Point(250,150))
stand2.draw(win)
stand3 = Line(Point(250,150),Point(250,180))
stand3.draw(win)
#drawing the empty lines for the word
x1 = 150
x2 = 180
l = 0
print(word)
while l< len(word):
wordLine = Line(Point(x1, 420),Point(x2,420))
wordLine.draw(win)
l+=1
x1+=40
x2+=40
guessCounter = 0
textCheck = 0
invalidText = Text(Point(600,100), 'You did not enter a valid letter.')
invalidText.setTextColor('red')
indexes = []
while guessCounter < 6:
#text entry box
textEntry = Entry(Point(600,180),10)
textEntry.draw(win)
guessText = Text(Point(600,150), 'Guess a letter:')
guessText.draw(win)
#user has to click this box to confirm the letter
enterBox = Rectangle(Point(580,200), Point(620,220))
enterBox.setFill('white')
enterBox.draw(win)
clickText = Text(Point(600,210), 'Enter')
clickText.draw(win)
click = win.getMouse()
x = click.getX()
y = click.getY()
if 580 < x < 620 and 200 < y < 220:
guess = textEntry.getText().lower().strip()
if guess not in alphabet:
if textCheck == 0:
invalidText.draw(win)
textCheck = 1
else:
if textCheck == 1:
invalidText.undraw()
textCheck = 0
for letter in word:
if letter == guess:
indexes.append(word.index(guess))
print(indexes)
win.getMouse()
win.close()
return returnStuff
#list with various words pertaining to nanotechnology
words = ['nanotechnology', 'science', 'nanometre' , 'strength', 'chemistry',
'small', 'molecule', 'light' , 'weight', 'technology', 'materials',
'property', 'physics', 'engineering', 'matter', 'waterloo', 'nanobot',
'reaction', 'structure', 'cells']
#picks a random word from the list
word = random.choice(words)
#this variable ensures it opens the game the first time
initialCall = 1
#stores the returnValue for the first call
returnValue = hangman(word)
#sets the initialCall to 0 after first call
if returnValue['1st']==1:
initialCall=0
#Calls the game function again if user wishes
while initialCall == 1 or returnStuff['again'] == 1:
returnValue = hangman(word)
I am making Hangman in Python graphics. I apologize for the huge block of code, it all works fine, I just thought it must be useful. The part of the code that I'm concerned about is this:
else:
if textCheck == 1:
invalidText.undraw()
textCheck = 0
for letter in word:
if letter == guess:
indexes.append(word.index(guess))
print(indexes)
This block of code will be executed when the user's letter guess is in the alphabet, I then run through each letter in the chosen word, and if at any point a letter in the word is the same as the guess letter, I store the index of that letter in a empty list so I can use that to tell the computer where to draw the letters on the empty lines.
It works fine, with the exception of when there is a duplicate letter in the word. For example, engineering has 3 es. Unfortunately, .index() only records the index for when the letter appears the first time, and it disregards the other letters. What is the work around for this so I can get the indexes of all 3 es in that word, instead of 3 indexes of just the first e. For testing purposes, I have printed the chosen word and the index list on the console so I can see what's going on and so I don't actually have to guess a letter.

you can do something like this
def indexes(word,letter):
for i,x in enumerate(word):
if x == letter:
yield i
test
>>> list( indexes("engineering","e") )
[0, 5, 6]
>>>
this function is a generator, that is a lazy function that only give result when asked for then, to get a individual result you use next, the functions is execute until the first yield then return the result and stop its execution and remember where it was, until another call to next is executed in witch point resume execution from the previous point until the next yield, if the is no more raise StopIteration, for example:
>>> word="engineering"
>>> index_e = indexes(word,"e")
>>> next(index_e)
0
>>> print(word)
engineering
>>> next(index_e)
5
>>> next(index_e)
6
>>> next(index_e)
Traceback (most recent call last):
File "<pyshell#13>", line 1, in <module>
next(index_e)
StopIteration
>>>
to update a list with the result of this function, you can use the extend method
>>> my_list=list()
>>> index_e = indexes(word,"e")
>>> my_list.extend( index_e )
>>> my_list
[0, 5, 6]
>>>
generator are used in cases where their result is only a intermediary step to something else because they use a minimum amount of memory, but in this case maybe you want the whole thing so use it as the first example or remake the function to return a list in the first place like this
def indexes(word,letter):
result = list()
for i,x in enumerate(word):
if x == letter:
result.append(i)
return result
sorry if I confuse you with the yield thing.

Related

How do I iterate through a list by the indices of a different list?

I want sub_main to be made from the 'sub' list as:
sub_main = ['c','d','e','f','j','k','l','m','n','o','p'...
I have tried:
for i in sub:
for j in main:
if j == i[0]:
while j != i[1]:
sub_main.append(j)
but it is an infinite loop for some reason
import string
sub_main = []
main = list(string.ascii_lowercase)
sub = [['c','f'],['j','p'],['r','t']]
import string
sub_main = []
main = list(string.ascii_lowercase)
sub = [['c', 'f'], ['j', 'p'], ['r', 't']]
indexed_main = {ch: i for i, ch in enumerate(main)}
for s in sub:
sub_main.extend(main[indexed_main[s[0]]:indexed_main[s[1]]+1])
import string
sub_main = []
main = list(string.ascii_lowercase)
sub = [['c','f'],['j','p'],['r','t']]
for chsub in sub:
amount = ord(chsub[1]) - ord(chsub[0])
remaining = amount
while remaining >= 0:
sub_main.append(main[ord(chsub[0])-ord('a') + amount - remaining])
remaining -= 1
This is how I would have done it, it uses the fact that every character has an ascii value.
Basically iterate over each sub-list in sub and get the amount of characters that are between the two in chsub.
main[ord(chsub[0])-ord('a') + amount - remaining] - This first finds the index of the character it should be on (c - a, for example, which is 2 which means it begins from main[2]).
Then inside that calculation I also check the difference between amount and remaining to see which character it needs to get past the initial character.
Result:
['c', 'd', 'e', 'f', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'r', 's', 't']
The reason for your infinite loop has been given in a comment to your question, you never change j in the while, but 3 loops is too much for this issue.

How do I find character frequency form text file through iteration? (python3)

I'm trying to find a way to iterate through a text file and list to find character frequency. I understand that I could use Count() for this. But Count() gives everything including spaces periods and whatnots. Also it does not show the character frequency in alphabetical order. I found a way to do it and it works but not really. I'll explain later. Also when I try to put the frequency I get a KeyError. I'll also explain.
I don't want to put my whole project on here so I'll explain some stuff first. I have a separate list called alphabet_list which includes the alphabet. There's a text file that is already read through and converted into uppercase called new_text.
Character frequency Code:
for i in range(len(alphabet_list)):
for c in new_text:
if c == alphabet_list[i]:
count += 1
else:
count = 0
print(alphbet_list[i] + " " + str(count)
i += 1
Output
A 0
A 0
.
.
.
A 1
A 0
.
.
.
B 0
.
.
.
B 1
B 2
B 0
.
.
.
Z 0
P.S the str(count) is temporarily there because I want to see how it looks like print out, I needed to store the result in dictionary
My output would be that, like I said it works but not really. It will iterate but it iterates through every letter and prints out the result already and does not iterate the whole text file and just print final result. It will add to the result if there is another letter same as before right next to each other. Ex (... bb...) it will be B 1, B 2 like shown in my output. And for some reason when I use return it doesn't work. It returns nothing and just ends the program.
Second Code with KeyError:
I skipped the problem on top because I couldn't find the answer and didn't want to waste my time but ran into another problem lol*
for i in range(len(alphabet_list)):
for c in new_text:
if c == alphabet_list[i]:
count += 1
else:
count = 0
c_freq[alphabet_list[i]] == count
print(c_freq)
i += 1
This one was pretty simple I got a KeyError: 'A'.
I tried only doing the
i = 3 #just random number to test
count = 50
c_freq[alphabet_list[i]] == count
print(c_freq)
and it works, so I'm thinking that problem is also related to the problem above(? maybe). Anyways any help would be great. Thanks!
Sorry for long question but I really needed help.
This should help you:
lst = ['A', 'Z', 'H', 'A', 'B', 'N', 'H', 'Y', '.' , ',','Z'] #Initial list. Note: The list also includes characters such as commas and full stops.
alpha_dict = {}
for ch in lst:
if ch.isalpha(): #Checks if the character is an alphabet
if ch in alpha_dict.keys():
alpha_dict[ch] += 1 #If key already exists, value is incremented by 1
else:
alpha_dict[ch] = 1 #If key does not exist, a new key is created with value 1
print(alpha_dict)
Output:
{'A': 2, 'Z': 2, 'H': 2, 'B': 1, 'N': 1, 'Y': 1}
Since you want the output to be sorted in alphabetical order, add these lines to your code:
key_list = list(alpha_dict.keys()) #Creates a list of all the keys in the dict
key_list.sort() #Sorts the list in alphabetical order
final_dict = {}
for key in key_list:
final_dict[key] = alpha_dict[key]
print(final_dict)
Output:
{'A': 2, 'B': 1, 'H': 2, 'N': 1, 'Y': 1, 'Z': 2}
Thus, here is the final code:
lst = ['A', 'Z', 'H', 'A', 'B', 'N', 'H', 'Y', '.' , ',','Z']
alpha_dict = {}
for ch in lst:
if ch.isalpha():
if ch in alpha_dict.keys():
alpha_dict[ch] += 1
else:
alpha_dict[ch] = 1
key_list = list(alpha_dict.keys())
key_list.sort()
final_dict = {}
for key in key_list:
final_dict[key] = alpha_dict[key]
print(final_dict)
Output:
{'A': 2, 'B': 1, 'H': 2, 'N': 1, 'Y': 1, 'Z': 2}

Printing a list where values are in a specific distance from each other using Python

I have a list,
A = ['A','B','C','D','E','F','G','H']
if user input x = 4, then I need an output that shows every value that is 4 distance away from each other.
If starting from 'A' after printing values that are 4 distance away from each other ie: {'A', 'E'}, the code should iterate back and start from 'B' to print all values from there ie: {'B', 'F'}
No number can be in more than one group
Any help is going to be appreciated since I am very new to python.
this is what I have done
x = input("enter the number to divide with: ")
A = ['A','B','C','D','E','F','G','H']
print("Team A is divided by " +x+ " groups")
print("---------------------")
out = [A[i] for i in range(0, len(A), int(x))]
print(out)
My code is printing only the following when user input x =4
{'A', 'E'}
But I need it to look like the following
{'A', 'E'}
{'B', 'F'}
{'C', 'G'}
{'D', 'H'}
what am I doing wrong?
Use zip:
out = list(zip(A, A[x:]))
For example:
x = 4 # int(input("enter the number to divide with: "))
A = ['A','B','C','D','E','F','G','H']
print(f"Team A is divided by {x} groups")
print("---------------------")
out = list(zip(A, A[x:]))
print(out)
Outputs:
[('A', 'E'), ('B', 'F'), ('C', 'G'), ('D', 'H')]
Here you have the live example
If you want to keep the comprehension:
out = [(A[i], A[i+x]) for i in range(0, len(A)-x)]
**You can find my answer below.
def goutham(alist):
for passchar in range(0,len(alist)-4):
i = alist[passchar]
j = alist[passchar+4]
print("{"+i+","+j+"}")
j = 0
alist = ['a','b','c','d','e','f','g','h']
goutham(alist)

Which character comes first?

So the input is word and I want to know if a or b comes first.
I can use a_index = word.find('a') and compare this to b_index = word.find('b') and if a is first, a is first is returned. But if b isn't in word, .find() will return -1, so simply comparing b_index < a_index would return b is first. This could be accomplished by adding more if-statements, but is there a cleaner way?
function description:
input: word, [list of characters]
output: the character in the list that appears first in the word
Example: first_instance("butterfly", ['a', 'u', 'e'] returns u
You can create a function that takes word and a list of chars - convert those chars into a set for fast lookup and looping over word take the first letter found, eg:
# Chars can be any iterable whose elements are characters
def first_of(word, chars):
# Remove duplicates and get O(1) lookup time
lookup = set(chars)
# Use optional default argument to next to return `None` if no matches found
return next((ch for ch in word if ch in lookup), None)
Example:
>>> first_of('bob', 'a')
>>> first_of('bob', 'b')
'b'
>>> first_of('abob', 'ab')
'a'
>>> first_of("butterfly", ['a', 'u', 'e'])
'u'
This way you're only ever iterating over word once and short-circuit on the first letter found instead of running multiple finds, storing the results and then computing the lowest index.
Make a list without the missing chars and then sort it by positions.
def first_found(word, chars):
places = [x for x in ((word.find(c), c) for c in chars) if x[0] != -1]
if not places:
# no char was found
return None
else:
return min(places)[1]
In any case you need to check the type of the input:
if isinstance(your_input, str):
a_index = your_input.find('a')
b_index = your_input.find('b')
# Compare the a and b indexes
elif isinstance(your_input, list):
a_index = your_input.index('a')
b_index = your_input.index('b')
# Compare the a and b indexes
else:
# Do something else
EDIT:
def first_instance(word, lst):
indexes = {}
for c in lst:
if c not in indexes:
indexes[c] = word.find(c)
else:
pass
return min(indexes, key=indexes.get)
It will return the character from list lst which comes first in the word.
If you need to return the index of this letter then replace the return statement with this:
return min_value = indexes[min(indexes, key=indexes.get)]

Musical note string (C#-4, F-3, etc.) to MIDI note value, in Python

The code in my answer below converts musical notes in strings, such as C#-4 or F-3, to their corresponding MIDI note values.
I am posting this because I am tired of trying to dig it up online every time I need it. I'm sure I'm not the only one who can find a use for it. I just wrote this up — it is tested and correct. It's in Python, but I feel that it pretty close to universally understandable.
#Input is string in the form C#-4, Db-4, or F-3. If your implementation doesn't use the hyphen,
#just replace the line :
# letter = midstr.split('-')[0].upper()
#with:
# letter = midstr[:-1]
def MidiStringToInt(midstr):
Notes = [["C"],["C#","Db"],["D"],["D#","Eb"],["E"],["F"],["F#","Gb"],["G"],["G#","Ab"],["A"],["A#","Bb"],["B"]]
answer = 0
i = 0
#Note
letter = midstr.split('-')[0].upper()
for note in Notes:
for form in note:
if letter.upper() == form:
answer = i
break;
i += 1
#Octave
answer += (int(midstr[-1]))*12
return answer
NOTES_FLAT = ['C', 'Db', 'D', 'Eb', 'E', 'F', 'Gb', 'G', 'Ab', 'A', 'Bb', 'B']
NOTES_SHARP = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B']
def NoteToMidi(KeyOctave):
# KeyOctave is formatted like 'C#3'
key = KeyOctave[:-1] # eg C, Db
octave = KeyOctave[-1] # eg 3, 4
answer = -1
try:
if 'b' in key:
pos = NOTES_FLAT.index(key)
else:
pos = NOTES_SHARP.index(key)
except:
print('The key is not valid', key)
return answer
answer += pos + 12 * (int(octave) + 1) + 1
return answer

Resources