Skip to content

Commit 1ef9bcb

Browse files
sozelfistvil02
andauthored
Implement Isogram (#816)
* feat: implement isogram * chore: use is_whitespace to check whitespace --------- Co-authored-by: Piotr Idzik <[email protected]>
1 parent af178ff commit 1ef9bcb

File tree

2 files changed

+106
-0
lines changed

2 files changed

+106
-0
lines changed

src/string/isogram.rs

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
//! This module provides functionality to check if a given string is an isogram.
2+
//! An isogram is a word or phrase in which no letter occurs more than once.
3+
4+
use std::collections::HashMap;
5+
6+
/// Enum representing possible errors that can occur while checking for isograms.
7+
#[derive(Debug, PartialEq, Eq)]
8+
pub enum IsogramError {
9+
/// Indicates that the input contains a non-alphabetic character.
10+
NonAlphabeticCharacter,
11+
}
12+
13+
/// Counts the occurrences of each alphabetic character in a given string.
14+
///
15+
/// This function takes a string slice as input. It counts how many times each alphabetic character
16+
/// appears in the input string and returns a hashmap where the keys are characters and the values
17+
/// are their respective counts.
18+
///
19+
/// # Arguments
20+
///
21+
/// * `s` - A string slice that contains the input to count characters from.
22+
///
23+
/// # Errors
24+
///
25+
/// Returns an error if the input contains non-alphabetic characters (excluding spaces).
26+
///
27+
/// # Note
28+
///
29+
/// This function treats uppercase and lowercase letters as equivalent (case-insensitive).
30+
/// Spaces are ignored and do not affect the character count.
31+
fn count_letters(s: &str) -> Result<HashMap<char, usize>, IsogramError> {
32+
let mut letter_counts = HashMap::new();
33+
34+
for ch in s.to_ascii_lowercase().chars() {
35+
if !ch.is_ascii_alphabetic() && !ch.is_whitespace() {
36+
return Err(IsogramError::NonAlphabeticCharacter);
37+
}
38+
39+
if ch.is_ascii_alphabetic() {
40+
*letter_counts.entry(ch).or_insert(0) += 1;
41+
}
42+
}
43+
44+
Ok(letter_counts)
45+
}
46+
47+
/// Checks if the given input string is an isogram.
48+
///
49+
/// This function takes a string slice as input. It counts the occurrences of each
50+
/// alphabetic character (ignoring case and spaces).
51+
///
52+
/// # Arguments
53+
///
54+
/// * `input` - A string slice that contains the input to check for isogram properties.
55+
///
56+
/// # Return
57+
///
58+
/// - `Ok(true)` if all characters appear only once, or `Ok(false)` if any character appears more than once.
59+
/// - `Err(IsogramError::NonAlphabeticCharacter) if the input contains any non-alphabetic characters.
60+
pub fn is_isogram(s: &str) -> Result<bool, IsogramError> {
61+
let letter_counts = count_letters(s)?;
62+
Ok(letter_counts.values().all(|&count| count == 1))
63+
}
64+
65+
#[cfg(test)]
66+
mod tests {
67+
use super::*;
68+
69+
macro_rules! isogram_tests {
70+
($($name:ident: $tc:expr,)*) => {
71+
$(
72+
#[test]
73+
fn $name() {
74+
let (input, expected) = $tc;
75+
assert_eq!(is_isogram(input), expected);
76+
}
77+
)*
78+
};
79+
}
80+
81+
isogram_tests! {
82+
isogram_simple: ("isogram", Ok(true)),
83+
isogram_case_insensitive: ("Isogram", Ok(true)),
84+
isogram_with_spaces: ("a b c d e", Ok(true)),
85+
isogram_mixed: ("Dermatoglyphics", Ok(true)),
86+
isogram_long: ("Subdermatoglyphic", Ok(true)),
87+
isogram_german_city: ("Malitzschkendorf", Ok(true)),
88+
perfect_pangram: ("Cwm fjord bank glyphs vext quiz", Ok(true)),
89+
isogram_sentences: ("The big dwarf only jumps", Ok(true)),
90+
isogram_french: ("Lampez un fort whisky", Ok(true)),
91+
isogram_portuguese: ("Velho traduz sim", Ok(true)),
92+
isogram_spanis: ("Centrifugadlos", Ok(true)),
93+
invalid_isogram_with_repeated_char: ("hello", Ok(false)),
94+
invalid_isogram_with_numbers: ("abc123", Err(IsogramError::NonAlphabeticCharacter)),
95+
invalid_isogram_with_special_char: ("abc!", Err(IsogramError::NonAlphabeticCharacter)),
96+
invalid_isogram_with_comma: ("Velho, traduz sim", Err(IsogramError::NonAlphabeticCharacter)),
97+
invalid_isogram_with_spaces: ("a b c d a", Ok(false)),
98+
invalid_isogram_with_repeated_phrase: ("abcabc", Ok(false)),
99+
isogram_empty_string: ("", Ok(true)),
100+
isogram_single_character: ("a", Ok(true)),
101+
invalid_isogram_multiple_same_characters: ("aaaa", Ok(false)),
102+
invalid_isogram_with_symbols: ("abc@#$%", Err(IsogramError::NonAlphabeticCharacter)),
103+
}
104+
}

src/string/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ mod boyer_moore_search;
55
mod burrows_wheeler_transform;
66
mod duval_algorithm;
77
mod hamming_distance;
8+
mod isogram;
89
mod isomorphism;
910
mod jaro_winkler_distance;
1011
mod knuth_morris_pratt;
@@ -31,6 +32,7 @@ pub use self::burrows_wheeler_transform::{
3132
};
3233
pub use self::duval_algorithm::duval_algorithm;
3334
pub use self::hamming_distance::hamming_distance;
35+
pub use self::isogram::is_isogram;
3436
pub use self::isomorphism::is_isomorphic;
3537
pub use self::jaro_winkler_distance::jaro_winkler_distance;
3638
pub use self::knuth_morris_pratt::knuth_morris_pratt;

0 commit comments

Comments
 (0)