// 1030: Group Elements by Key โ HashMap<K, Vec<V>>
// The classic group-by pattern using Entry API
use std::collections::HashMap;
/// Group words by their first character
fn group_by_first_letter() {
let words = vec!["apple", "avocado", "banana", "blueberry", "cherry"];
let mut groups: HashMap<char, Vec<&str>> = HashMap::new();
for word in &words {
let key = word.chars().next().unwrap();
groups.entry(key).or_default().push(word);
}
assert_eq!(groups[&'a'], vec!["apple", "avocado"]);
assert_eq!(groups[&'b'], vec!["banana", "blueberry"]);
assert_eq!(groups[&'c'], vec!["cherry"]);
}
/// Group numbers by parity
fn group_by_parity() {
let nums = vec![1, 2, 3, 4, 5, 6, 7, 8];
let mut groups: HashMap<&str, Vec<i32>> = HashMap::new();
for &n in &nums {
let key = if n % 2 == 0 { "even" } else { "odd" };
groups.entry(key).or_default().push(n);
}
assert_eq!(groups["even"], vec![2, 4, 6, 8]);
assert_eq!(groups["odd"], vec![1, 3, 5, 7]);
}
/// Generic group_by function
fn group_by<T, K, F>(items: &[T], key_fn: F) -> HashMap<K, Vec<&T>>
where
K: std::hash::Hash + Eq,
F: Fn(&T) -> K,
{
let mut groups: HashMap<K, Vec<&T>> = HashMap::new();
for item in items {
groups.entry(key_fn(item)).or_default().push(item);
}
groups
}
fn test_generic_group_by() {
let data = vec![("Alice", 90), ("Bob", 85), ("Alice", 92), ("Bob", 88)];
let groups = group_by(&data, |&(name, _)| name);
assert_eq!(groups["Alice"].len(), 2);
assert_eq!(groups["Bob"].len(), 2);
assert_eq!(groups["Alice"][0].1, 90);
assert_eq!(groups["Alice"][1].1, 92);
}
fn main() {
group_by_first_letter();
group_by_parity();
test_generic_group_by();
println!("โ All tests passed");
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_group_by_first_letter() { group_by_first_letter(); }
#[test]
fn test_group_by_parity() { group_by_parity(); }
#[test]
fn test_generic() { test_generic_group_by(); }
#[test]
fn test_group_by_length() {
let words = vec!["hi", "hey", "hello", "yo", "yes"];
let groups = group_by(&words, |w| w.len());
assert_eq!(groups[&2].len(), 2); // "hi", "yo"
assert_eq!(groups[&3].len(), 2); // "hey", "yes"
assert_eq!(groups[&5].len(), 1); // "hello"
}
}
(* 1030: Group Elements by Key *)
(* Build a map from key to list of values *)
module StringMap = Map.Make(String)
(* Approach 1: Group words by first letter *)
let group_by_first_letter () =
let words = ["apple"; "avocado"; "banana"; "blueberry"; "cherry"] in
let groups = List.fold_left (fun acc w ->
let key = String.make 1 w.[0] in
let current = match StringMap.find_opt key acc with
| Some lst -> lst
| None -> []
in
StringMap.add key (current @ [w]) acc
) StringMap.empty words in
assert (StringMap.find "a" groups = ["apple"; "avocado"]);
assert (StringMap.find "b" groups = ["banana"; "blueberry"]);
assert (StringMap.find "c" groups = ["cherry"])
(* Approach 2: Group numbers by property *)
let group_by_parity () =
let nums = [1; 2; 3; 4; 5; 6; 7; 8] in
let groups = List.fold_left (fun acc n ->
let key = if n mod 2 = 0 then "even" else "odd" in
let current = match StringMap.find_opt key acc with
| Some lst -> lst
| None -> []
in
StringMap.add key (current @ [n]) acc
) StringMap.empty nums in
assert (StringMap.find "even" groups = [2; 4; 6; 8]);
assert (StringMap.find "odd" groups = [1; 3; 5; 7])
(* Approach 3: Generic group_by function *)
let group_by (type a) key_fn (items : a list) =
List.fold_left (fun acc item ->
let key = key_fn item in
let current = match StringMap.find_opt key acc with
| Some lst -> lst
| None -> []
in
StringMap.add key (current @ [item]) acc
) StringMap.empty items
let test_generic_group_by () =
let data = [("Alice", 90); ("Bob", 85); ("Alice", 92); ("Bob", 88)] in
let groups = group_by fst data in
assert (StringMap.find "Alice" groups = [("Alice", 90); ("Alice", 92)]);
assert (StringMap.find "Bob" groups = [("Bob", 85); ("Bob", 88)])
let () =
group_by_first_letter ();
group_by_parity ();
test_generic_group_by ();
Printf.printf "โ All tests passed\n"