Skip to content

Commit

Permalink
Switch tally from String to Box<str> for words
Browse files Browse the repository at this point in the history
Save a bit of overhead with normalized, immutable words in the tally.
  • Loading branch information
havenwood committed Sep 14, 2024
1 parent 5544cd7 commit d6b2a46
Show file tree
Hide file tree
Showing 2 changed files with 20 additions and 20 deletions.
20 changes: 10 additions & 10 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
//!
//! let input = "Cinquedea".as_bytes();
//! let words = WordTally::new(input, Case::Lower, Sort::Desc, Filters::default());
//! let expected_tally = vec![("cinquedea".to_string(), 1)].into_boxed_slice();
//! let expected_tally = vec![(Box::from("cinquedea"), 1)].into_boxed_slice();
//!
//! assert_eq!(words.tally(), expected_tally);
//! ```
Expand All @@ -44,7 +44,7 @@ use unicode_segmentation::UnicodeSegmentation;
#[non_exhaustive]
pub struct WordTally {
/// Ordered pairs of words and the count of times they appear.
tally: Box<[(String, u64)]>,
tally: Box<[(Box<str>, u64)]>,

/// The sum of all words tallied.
count: u64,
Expand All @@ -66,7 +66,7 @@ impl Hash for WordTally {
}

/// A `tally` supports `iter` and can also be represented as a `Vec`.
impl From<WordTally> for Vec<(String, u64)> {
impl From<WordTally> for Vec<(Box<str>, u64)> {
fn from(word_tally: WordTally) -> Self {
word_tally.tally.into_vec()
}
Expand Down Expand Up @@ -228,7 +228,7 @@ impl WordTally {
}

/// Gets the `tally` field.
pub fn tally(self) -> Box<[(String, u64)]> {
pub fn tally(self) -> Box<[(Box<str>, u64)]> {
self.tally
}

Expand All @@ -254,7 +254,7 @@ impl WordTally {
}

/// Creates a tally of normalized words from an input that implements `Read`.
fn tally_map<T: Read>(input: T, case: Case, min_chars: MinChars) -> IndexMap<String, u64> {
fn tally_map<T: Read>(input: T, case: Case, min_chars: MinChars) -> IndexMap<Box<str>, u64> {
let mut tally = IndexMap::new();
let lines = BufReader::new(input).lines();

Expand All @@ -272,7 +272,7 @@ impl WordTally {
}

/// Removes words from the `tally_map` based on any word `Filters`.
fn filter(tally_map: &mut IndexMap<String, u64>, filters: Filters, case: Case) {
fn filter(tally_map: &mut IndexMap<Box<str>, u64>, filters: Filters, case: Case) {
// Remove any words that lack the minimum number of characters.
if filters.min_count.applicable() {
tally_map.retain(|_, &mut count| count >= filters.min_count.0);
Expand All @@ -298,11 +298,11 @@ impl WordTally {
}

/// Normalizes word case if a `Case` other than `Case::Original` is provided.
fn normalize_case(word: &str, case: Case) -> String {
fn normalize_case(word: &str, case: Case) -> Box<str> {
match case {
Case::Lower => word.to_lowercase(),
Case::Upper => word.to_uppercase(),
Case::Original => word.to_owned(),
Case::Lower => word.to_lowercase().into_boxed_str(),
Case::Upper => word.to_uppercase().into_boxed_str(),
Case::Original => Box::from(word),
}
}
}
20 changes: 10 additions & 10 deletions tests/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ fn word_tally_test(case: Case, sort: Sort, filters: Filters, fields: &ExpectedFi
let expected_tally = fields
.tally
.iter()
.map(|(word, count)| ((*word).to_string(), *count))
.map(|(word, count)| (Box::from(*word), *count))
.collect::<Vec<_>>()
.into_boxed_slice();
assert_eq!(word_tally.tally(), expected_tally);
Expand Down Expand Up @@ -279,11 +279,11 @@ fn vec_from() {
assert_eq!(
Vec::from(tally),
vec![
("c".to_string(), 15),
("d".to_string(), 11),
("123".to_string(), 9),
("b".to_string(), 7),
("a".to_string(), 3)
(Box::from("c"), 15),
(Box::from("d"), 11),
(Box::from("123"), 9),
(Box::from("b"), 7),
(Box::from("a"), 3)
]
);
}
Expand All @@ -299,9 +299,9 @@ fn test_excluding_words() {
let tally = WordTally::new(input, Case::Lower, Sort::Unsorted, filters);
let result = tally.tally();

assert!(result.iter().any(|(word, _)| word == "tree"));
assert!(!result.iter().any(|(word, _)| word == "heaven"));
assert!(!result.iter().any(|(word, _)| word == "hell"));
assert!(result.iter().any(|(word, _)| word.as_ref() == "tree"));
assert!(!result.iter().any(|(word, _)| word.as_ref() == "heaven"));
assert!(!result.iter().any(|(word, _)| word.as_ref() == "hell"));
}

#[test]
Expand All @@ -315,7 +315,7 @@ fn test_only_words() {
let tally = WordTally::new(input, Case::Lower, Sort::Desc, filters);
let result = tally.tally();

let expected = vec![("chaos".to_string(), 2), ("star".to_string(), 1)].into_boxed_slice();
let expected = vec![(Box::from("chaos"), 2), (Box::from("star"), 1)].into_boxed_slice();

assert_eq!(result, expected);
}
Expand Down

0 comments on commit d6b2a46

Please sign in to comment.