Checking if a word is an isogram - python

I wrote a program to check if a word is an isogram but after passing the test cases, it says "Your solution failed to pass all the tests"
below is the test cases:
from unittest import TestCase
class IsogramTestCases(TestCase):
def test_checks_for_isograms(self):
word = 'abolishment'
self.assertEqual(
is_isogram(word),
(word, True),
msg="Isogram word, '{}' not detected correctly".format(word)
)
def test_returns_false_for_nonisograms(self):
word = 'alphabet'
self.assertEqual(
is_isogram(word),
(word, False),
msg="Non isogram word, '{}' falsely detected".format(word)
)
def test_it_only_accepts_strings(self):
with self.assertRaises(TypeError) as context:
is_isogram(2)
self.assertEqual(
'Argument should be a string',
context.exception.message,
'String inputs allowed only'
)
and also below is my code for the tests. it passed the tests but failed some hidden tests:
def is_isogram(word):
if type(word) == str or len(word) != 0:
if not word:
return (word, False)
word = word.lower()
return (word, len(set(word)) == len(word))
else:
raise TypeError('Argument should be a string')
can anybody tell me what I am doing wrong?

As this question relates to something i have been working on, i would like to share my findings, so that anyone who is looking for the clear solution of this question uses it.
def is_isogram(word):
word = word.lower()
try:
if len(word) > 0:
for letter in word:
if word.count(letter) > 1:
return (word, False)
return (word, True)
else:
return ('argument', False)
except TypeError as e:
return "Argument should be a string: "+ str(e)
print is_isogram("")

Ok this works, passed all hidden tests too. You are welcome
def is_isogram(word):
'''This function tests for isogram'''
word_set = set(word)
if word.strip() == "":
return (word, False)
elif len(word) == len(word_set):
return (word, True)
elif type(word)!= str :
raise TypeError
else:
return (word, False)

def is_isogram(word):
return (word,True) if word and len(set(word)) == len(word) else (word,False)

Related

Getting Assertion Error but code output matches assert

My problem is that when i run my code with the input 'word1 word2 word3 word2' (for example) it works as intended, meaning it removes a word if it finds a duplicate of that word in the string.
I have no clue why i get assertion error why i try to run:
set1 = StringSet('word1 word2 word3 word2')
assert str(set1) == 'word1 word2 word3'
output:
assert str(set1) == 'word1 word2 word3'
AssertionError
if i remove the assert and print set1 the code works.
'word1 word2 word3'
Thanks in regards.
My code:
class StringSet:
def __init__(self, a_string=""):
self.a_string = a_string
self.__words = []
for word in a_string.split(" "):
if word not in self.__words:
self.__words.append(word)
def __str__(self):
s = ""
for word in self.__words:
s+= f"{word}"
s+=" "
return "{}".format(s)
def size(self):
counter = 0
for word in self.__words:
counter += 1
return f"{counter}"
def __add__(self, other):
s = ""
new_string = self.a_string + " " + other.a_string
new_set = StringSet(new_string)
return new_set
def make_list(self):
pass
def query(self, a_string=""):
new_string = StringSet(a_string)
return new_string
def at(self):
pass

Incorrect return value, not getting a 'True' return, Python

I am expecting a return of True, but is getting a return of None.
I have put some print statement in the code to help debug. It shows that the 'print("Got True")' statement ran, so I know the code ended up in the right branch of the code just before 'return True' but for some reason I am getting 'None'. However 'return False' works perfectly when I put in a word that isn't a palindrome.
Thanks for the help.
def first(word):
return word[0]
def last(word):
return word[-1]
def middle(word):
return word[1:-1]
def is_palindrome(word):
print(word)
if len(word) <= 1:
print("Got True")
return True
else:
print(len(word))
if first(word) == last(word):
is_palindrome(middle(word))
else:
print("Got False")
return False
print(is_palindrome('allen'))
print("\n")
print(is_palindrome('redivider'))
Output:
allen
5
Got False
False
redivider
9
edivide
7
divid
5
ivi
3
v
Got True
None
You need to return in every branch of your function, e.g.:
def is_palindrome(word):
print(word)
if len(word) <= 1:
print("Got True")
return True
else:
print(len(word))
if first(word) == last(word):
return is_palindrome(middle(word)) # **Added return**
else:
print("Got False")
return False
But you can simplify the structure because if you return then you don't need the else: clause because it can't be reached, so this can be written:
def is_palindrome(word):
print(word)
if len(word) <= 1:
print("Got True")
return True
print(len(word))
if first(word) == last(word):
return is_palindrome(middle(word))
print("Got False")
return False
Even in a recursive function, you have to use a return statement to return a value:
if first(word) == last(word):
return is_palindrome(middle(word))
You should return the result in every possible conditional branches. You can use directly return statement or capture the result of the is_palindrome function in some variable and return that if it is confusing.

Custom build validation statement on an iterator

What's the quickest way (processing sense) to iterate over a list and validate them according to set flags?
Or, in other words, what's the optimal approach to filter list with statement depending on configuration.
Example below is on a list of strings and depending on flags I'd like to filter some of them.
class Validator(object):
def __init__(self, digit=False, tag=False, short=False):
self.digit = digit
self.tag = tag
self.short = short
def __call__(self, words):
good_words = []
for word in words:
if self.digit:
if word.is_digit(): continue
if self.tag:
if word[0] == "<": continue
if self.short:
if len(word) < 3: continue
good_words.append(word)
return good_words
Use of Validator
val = Validator(digit=True, short=True)
words = "An apple a day is 20 dollars a month"
print(val(words))
# ["apple", "day", "dollars", "month"]
To avoid creating a new (and potentially long) list, you can instead make a generator function with yield for each succeeded word instead of return for a whole list:
class Validator(object):
def __init__(self, digit=False, tag=False, short=False, *more_filters):
self.digit = digit
self.tag = tag
self.short = short
self.more_filters= more_filters
def __call__(self, words):
for word in words:
if self.digit:
if word.is_digit(): continue
if self.tag:
if word[0] == "<": continue
if self.short:
if len(word) < 3: continue
if any(f(word) for f in self.more_filters):
continue
yield word
Use:
other_tests = [lambda w: w.startswith('#'), lambda w: w.endswith('?')]
val = Validator(digit=True, short=True, *other_tests)
words = "An apple a day is 20 dollars a month #healty toomuch? livelong!"
print(val(words))
# apple day dollars month livelong!
Element by element approach using python filter
from itertools import filterfalse
class Validator(object):
def __init__(self, digit=False, tag=False, short=False):
self.digit = digit
self.tag = tag
self.short = short
def __call__(self, word):
if self.digit:
if word.isdigit(): return True
if self.tag:
if word[0] == "<": return True
if self.short:
if len(word) < 3: return True
return False
val = Validator(digit=True, short=True)
words = "An apple a day is 20 dollars a month".split()
assert list(filter(val, words)) == ['An', 'a', 'is', '20', 'a']
assert list(filterfalse(val, words)) == ['apple', 'day', 'dollars', 'month']

Determining if a string is an Isogram

The Task:
Write a program that checks if a word supplied as the argument is an Isogram. An Isogram is a word in which no letter occurs more than once.
Create a method called is_isogram that takes one argument, a word to test if it's an isogram. This method should return a tuple of the word and a boolean indicating whether it is an isogram.
If the argument supplied is an empty string, return the argument and False: (argument, False). If the argument supplied is not a string, raise a TypeError with the message 'Argument should be a string'.
Example:
is_isogram("abolishment")
Expected result:
("abolishment", True)
The Visible test
from unittest import TestCase
class IsogramTestCases(TestCase):
def test_checks_for_isograms(self):
word = 'abolishment'
self.assertEqual(
is_isogram(word),
(word, True),
msg="Isogram word, '{}' not detected correctly".format(word)
)
def test_returns_false_for_nonisograms(self):
word = 'alphabet'
self.assertEqual(
is_isogram(word),
(word, False),
msg="Non isogram word, '{}' falsely detected".format(word)
)
def test_it_only_accepts_strings(self):
with self.assertRaises(TypeError) as context:
is_isogram(2)
self.assertEqual(
'Argument should be a string',
context.exception.message,
'String inputs allowed only'
)
My Solution:
def is_isogram(word):
if type(word) != str:
raise TypeError('Argument should be a string')
elif word == "":
return (word, False)
else:
word = word.lower()
for char in word:
if word.count(char) > 1:
return (word, False)
else:
return (word, True)
But it the function refuses to pass some hidden test: What is wrong with my solution? Is there another elegant way of writing this function?
I'm not sure your search should be case insensitive so perhaps you should remove word = word.lower(), but your main issue is return terminates the function so you current code needs to only return True after all tests have been conducted (i.e. outside of the loop):
for char in word:
if word.count(char) > 1:
return (word, False)
return (word, True)
Anyway, a better way is to use set() to remove all duplicates from your string and then compare the lengths; also use isinstance() to check if word is a string. You can use if w to check for empty strings . You don't need parentheses with return, the comma is enough to return a tuple:
def is_isogram(word):
if isinstance(word,str):
w = word.lower() # assuming you want a case in-sensitive search
return word, len(w) == len(set(w)) if w else False
else:
raise TypeError('Argument should be a string')
Examples:
is_isogram('abolishment')
# ('abolishment', True)
is_isogram('happy')
# ('happy', False)
is_isogram('')
# ('', False)
is_isogram(1)
# TypeError: Argument should be a string
I have tried in this way:
def isogram(n):
if not isinstance(n, str):
return n,False
elif len(n) < 1:
return n,False
n = n.lower()
if len(n) == len(set(n)):
return n,True
else:
return n,False
The second line checks if the input n is a string, if it isn't it returns False.
The line 4 checks if the length of the input is smaller than 1, in that case the input is empty and we cannot check if it is an isogram or not.
'n = n.lower()' in line 6 assures us that the input becomes a string with lowercase letters.
In line 7 we check if the length of the input is equal to the length of the set(n). The function set() converts a collection or a sequence or an iterator object into a set. For example: set('lists') returns {'s', 't', 'l', 'i'}, as you can see the letter 's' which appears twice in 'lists', does not appear in the set. This is useful to check if the length of the set is equal to the length of the input, if there is a letter which appears twice in the input the condition is False.
I hope this explanation makes the script clear.
def is_isogram(string):
if type(string) != str:
raise TypeError('Argument should be a string')
elif string == " ":
return (string, False)
else:
for i in string:
if string.count(i) > 1:
return (string, False)
return (string, True)
The reason for this
"But it the function refuses to pass some hidden test: What is wrong with my solution? Is there another elegant way of writing this function?"
i.e refuses to pass the tests is pretty simple.
elif word == "":
should be
elif word == " ":
as per the unit test
if argument==" " :
return(argument,False)
You are forgetting the whitespace. Hope this helps the next person who comes searching.
This works for me. If there are more than one digit it will be added to a new string. If this new string is empty the function will return True. Else False. Only " if word.count(char) > 1: " won´t do it. At least not for me.
def isagram (word):
if not isinstance(word, str):
return (False, word)
if not word:
return (False, word)
word.lower()
multi_char = ''
for char in word:
if word.count(char) > 1:
multi_char += char
return (False, word) if multi_char else (True, word)
Here is one way to solve the problem:
def is_isogram(argument):
word_seen=set()
if type(argument) != str:
raise TypeError('Argument should be a string')
if argument==" " :
return(argument,False)
argument.lower()
for letter in argument:
if letter in word_seen:
return(argument,False)
word_seen.add(letter)
return (argument,True)
Equivalently, I also tried the following to remove all spaces in strings:
def is_isogram(argument):
word_seen=set()
if type(argument) != str:
raise TypeError('Argument should be a string')
if argument==" " :
return(argument,False)
argument.lower()
argument = ''.join(argument.split())
for letter in argument:
if letter in word_seen:
return(argument,False)
word_seen.add(letter)
return (argument,True)
i solved it here:
def is_isogram(word):
for letter in word:
if word.count(letter) > 1:
return False
else:
return True
print is_isogram("Hello")
def is_Isogram(words):
cover_words = words.lower()
letter_list = []
for letter in cover_words:
if letter.isalpha():
if letter in letter_list:
return False
letter_list.append(letter)
return True
if __name__ == '__main__':
print(is_Isogram('Machine'))
print(is_Isogram('Geeks'))`
This is the last part of your suggested solution:
for char in word:
if word.count(char) > 1:
return (word, False)
else:
return (word, True)
return will end the function, hence the for loop will always exit after counting repetitions of the first letter and never check the other ones.
If your word has a first letter which is unique, the function will return True.
I believe this is a bug and this is my corrected suggestion:
for char in word:
if word.count(char) > 1:
return (word, False)
return (word, True)
import collections
def is_isogram(word):
if not isinstance(word, str):
raise TypeError('Argument should be a string')
most_common = collections.Counter(word).most_common(1)
if most_common:
char, freq = most_common[0]
return (word, freq == 1)
else:
return (word, False)
You just need an extra conditional statement to check if the characters in the word are solely alphabets (i.e. no numbers or symbols) whilst checking if the characters are repeated:
def is_isogram(word):
if type(word) != str:
raise TypeError("Argument should be a string")
if len(word) < 1:
return (word, False)
word = word.lower()
for char in word:
if word.count(char) > 1 or \
char not in "abcdefghijklmnopqrstuvwxyz": # this checks that the char is an alphabet
return (word, False)
return (word, True)
I guess this should help.
i found this correct
def is_isogram(word):
if type(word)!=str:
raise TypeError("Argument should be a String")
elif word.strip()=="":
return(word,False) else:
word =word.lower()
for char in word:
if word.count(char) >1:
return (word ,False)
else:
return(word ,True)

Python Palindrome Try and Except not printing

My except won't print when len(line.strip()) == d gets None.
def isPalindrome(word):
if len(word) < 1:
return True
else:
if word[0] == word[-1]:
return isPalindrome(word[1:-1])
else:
return False
def fileInput(filename):
palindromes = False
fh = open(filename, "r")
length = input("Enter length of palindromes:")
d = int(length)
try:
for line in fh:
for s in str(len(line)):
if isPalindrome(line.strip()):
palindromes = True
if (len(line.strip()) == d):
print(line.strip())
except:
print("No palindromes found for length entered.")
finally:
fh.close()
Your code is failing because your exception is not the only place where non-existence of d-length palindromes in your input file takes you.
You need to check for the value of palindromes as well.
So, at the end of your try-block, add a line that prints "no palindromes found", like so:
def fileInput(filename):
palindromes = False
# more code
try:
# more code
if not palindromes:
print("No palindromes found for length entered.")
except:
print("No palindromes found for length entered.")
finally:
# more code
By the way, I would clean up your function as follows:
def isPalindrome(word):
if not len(word): # is the same as len(word) == 0
return True
elif word[0] == word[-1]: # no need for overly nested if-else-blocks
return isPalindrome(word[1:-1])
else:
return False
def fileInput(filename):
palindromes = False
d = int(input("Enter length of palindromes:"))
with open(filename) as fh: # defaults to "r" mode. Also, takes care of closing the file for you
for line in fh:
word = line.strip()
if isPalindrome(word) and len(word) == d: # note that you didn't have the len(word)==d check before. Without that, you don't check for the length of the palindrome
palindromes = True
print(word)
if not palindromes:
print("No palindromes found for length entered.")

Categories