Skip to content

Commit

Permalink
Complete Anagram Finder implementation with tests and docstrings
Browse files Browse the repository at this point in the history
  • Loading branch information
Frank2446-dotcom committed Jan 11, 2025
1 parent 8bfcdb5 commit 29e7a74
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 36 deletions.
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -123,5 +123,5 @@
"source.organizeImports.ruff": "explicit"
}
},
"cSpell.words": ["dcbae"]
"cSpell.words": ["dcbae", "doctests"]
}
60 changes: 46 additions & 14 deletions solutions/anagram_finder.py
Original file line number Diff line number Diff line change
@@ -1,34 +1,66 @@
"""
Anagram Finder
This script provides a function to determine if two strings are anagrams.
Anagram Finder Module
This module provides a function to determine if two strings are anagrams of each other.
It checks if the strings are anagrams, handling edge cases like empty strings, special characters, and case-insensitivity.
Created on 03 01 2025
@author: Frankline Ambetsa
"""

import doctest


def is_anagram(string1: str, string2: str) -> bool:
"""
Check if two strings are anagrams of each other.
Anagrams are words or phrases made by rearranging the letters of another word or phrase.
This function will check for anagram status while ignoring spaces and case sensitivity.
Args:
string1 (str): The first string.
string2 (str): The second string.
Returns:
bool: True if the strings are anagrams, False otherwise.
Raises:
ValueError: If any string is empty except when both are empty.
>>> is_anagram("listen", "silent")
True
>>> is_anagram("evil", "vile")
True
>>> is_anagram("hello", "world")
False
>>> is_anagram("a gentleman", "elegant man")
True
>>> is_anagram("clint eastwood", "old west action")
True
"""
# Normalize strings: convert to lowercase and remove spaces
string1 = "".join(string1.lower().split())
string2 = "".join(string2.lower().split())

# Compare sorted characters
# Defensive assertions for valid string inputs
assert isinstance(string1, str), "string1 must be a string"
assert isinstance(string2, str), "string2 must be a string"

# If any string is empty, raise an error, unless both are empty
if string1 == "" and string2 == "":
return True # Both empty strings are considered anagrams of each other

if string1 == "":
raise AssertionError("string1 cannot be empty")
if string2 == "":
raise AssertionError("string2 cannot be empty")

# Remove spaces and convert to lowercase
string1 = string1.replace(" ", "").lower()
string2 = string2.replace(" ", "").lower()

# Check if sorted strings are equal (anagram check)
return sorted(string1) == sorted(string2)


# Run doctests when executed directly
if __name__ == "__main__":
# Example usage
word1 = "listen"
word2 = "silent"
print(f"Are '{word1}' and '{word2}' anagrams? {is_anagram(word1, word2)}")

word3 = "hello"
word4 = "world"
print(f"Are '{word3}' and '{word4}' anagrams? {is_anagram(word3, word4)}")
doctest.testmod()
55 changes: 34 additions & 21 deletions solutions/tests/test_anagram_finder.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
import unittest
"""
Unit tests for the `is_anagram` function.
These tests ensure correct behavior of the `is_anagram` function,
including standard anagram checking, edge cases, input validation, and defensive assertions.
Created on 03 01 2025
@author: Frankline Ambetsa
"""

import unittest
from solutions.anagram_finder import is_anagram


Expand Down Expand Up @@ -33,33 +42,37 @@ def test_different_lengths(self):

def test_empty_strings(self):
"""Test edge cases with empty strings."""
self.assertTrue(is_anagram("", ""))
self.assertFalse(is_anagram("a", ""))
self.assertFalse(is_anagram("", "a"))
self.assertTrue(
is_anagram("", "")
) # Both empty strings should be considered anagrams
with self.assertRaises(AssertionError):
is_anagram(
"a", ""
) # A non-empty string and empty string shouldn't be anagrams
with self.assertRaises(AssertionError):
is_anagram(
"", "b"
) # An empty string and non-empty string shouldn't be anagrams

def test_special_characters(self):
"""Test cases with special characters and spaces."""
self.assertTrue(is_anagram("a gentleman", "elegant man"))
self.assertTrue(is_anagram("clint eastwood", "old west action"))
self.assertFalse(is_anagram("hello world", "world hello!"))

def test_dummy(self):
"""Dummy test to ensure the test suite runs without errors."""
self.assertEqual(1, 1) # Trivial assertion to satisfy the test framework
def test_defensive_assertions(self):
"""Test defensive assertions for invalid inputs."""
with self.assertRaises(AssertionError):
is_anagram(123, "silent") # Non-string input
with self.assertRaises(AssertionError):
is_anagram("listen", 123) # Non-string input

with self.assertRaises(AssertionError):
is_anagram("a", "") # Empty string as second argument
with self.assertRaises(AssertionError):
is_anagram("", "b") # Empty string as first argument

if __name__ == "__main__":
# Run the tests and capture results
result = unittest.TextTestRunner().run(
unittest.TestLoader().loadTestsFromTestCase(TestAnagramFinder)
)

# Print the results summary with "Failed: 0"
print("\nTest Summary:")
print(f"Tests Run: {result.testsRun}")
print(f"Failed: {len(result.failures)}")
print(f"Errors: {len(result.errors)}")
if result.wasSuccessful():
print("All tests passed!")
else:
print("Some tests failed.")
if __name__ == "__main__":
# Run the tests
unittest.main()

0 comments on commit 29e7a74

Please sign in to comment.