From 567d8f5a960c5fcec36afa6f1281e86961e6353b Mon Sep 17 00:00:00 2001 From: Ed Guloien Date: Tue, 12 May 2026 18:16:04 -0400 Subject: [PATCH] init --- .gitignore | 1 + Cargo.lock | 7 ++++ Cargo.toml | 6 ++++ src/main.rs | 101 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 115 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 src/main.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..490f027 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "word-count" +version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..2d7098a --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "word-count" +version = "0.1.0" +edition = "2024" + +[dependencies] diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..682eed0 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,101 @@ + +// Write some functions to: +// 1. tokenize a string +// 2. manually make a map of tokens:their_counts +// 3. analyze the token structure: +// 3.a. count the words +// 3.b. count the unique words +// 3.c. get the first n most frequently used words +// 3.d. count the average word length +// 4. (pretty) print the info + +const PUNC: &str = ",.?:;'~|!@#$%^&*\""; + + +// todo: what about CTRL chars? +// todo: what about feeding Bytes? +// todo: return Result for whitespace input +fn tokenize(input: &str) -> Vec { + // filter punctuation + let mut s = String::new(); + for c in input.chars() { + if !PUNC.contains(c) { + s.push(c.to_lowercase().next().unwrap()); + } + } + + // convert to vec of strings + s.split_whitespace().map(|l| l.to_string()).collect() +} + +// todo: can the input type be better? +fn count_words(input: &[String]) -> Vec<(String,usize)> { + let mut counts : Vec<(String, usize)> = Vec::new(); + for w in input { + match counts.iter_mut().find(|(c,_)| c == w) { + Some((_, count)) => *count += 1, + _ => counts.push((w.to_string(), 1)), + } + } + counts.sort_by(|a,b| b.1.cmp(&a.1)); + counts +} + +fn analyze_word_count(input: &[(String,usize)]) -> usize { + let mut count = 0; + input.iter().for_each(|(_,c)| { count+=c }); + count +} + +fn analyze_unique_word_count(input: &[(String,usize)]) -> usize { + input.len() +} + +fn average_word_length(input: &[(String, usize)]) -> usize { + let mut len = 0; + let mut tokens = 0; + input.iter().for_each(|(s,c)| { + len += s.len() * c; + tokens += c; + }); + + len/tokens +} + +fn top_n_most_used(input: &[(String, usize)], n: usize) -> &[(String, usize)] { + let n = n.min(input.len()); + &input[..n] +} + +fn print(input: &[(String, usize)]) { + let counts = input; + let wc = analyze_word_count(&counts); + + // todo: clean this up a bit + println!("Word Count % of total"); + println!("---------------------"); + for (w, c) in counts { + println!("{} {} {}", w, c, *c as f64/(wc as f64)); + } +} + +fn main() { + let tokens = tokenize("Hello, world! hello"); + assert_eq!(tokens.len(), 3); + + let counts = count_words(&tokens); + let wc = analyze_word_count(&counts); + assert_eq!(wc, 3); + + let uwc = analyze_unique_word_count(&counts); + assert_eq!(uwc, 2); + + let avg_len = average_word_length(&counts); + assert_eq!(avg_len, 5); + + let top_n = top_n_most_used(&counts, 4); + assert_eq!(top_n[0].0, "hello"); + + print(&counts); + +}