๐Ÿฆ€ Functional Rust

1031: Count Frequencies

Difficulty: Beginner Category: Data Structures Concept: Frequency counting with hash maps โ€” the most common Entry API use case Key Insight: `*counts.entry(x).or_insert(0) += 1` is Rust's idiomatic one-liner for counting, combining lookup, default insertion, and mutation in a single expression.
// 1031: Count Frequencies
// The classic frequency counting pattern with Entry API

use std::collections::HashMap;

/// Count character frequencies
fn char_frequency() {
    let text = "abracadabra";

    let mut counts: HashMap<char, usize> = HashMap::new();
    for ch in text.chars() {
        *counts.entry(ch).or_insert(0) += 1;
    }

    assert_eq!(counts[&'a'], 5);
    assert_eq!(counts[&'b'], 2);
    assert_eq!(counts[&'r'], 2);
    assert_eq!(counts[&'c'], 1);
    assert_eq!(counts[&'d'], 1);
}

/// Count word frequencies using and_modify
fn word_frequency() {
    let words = vec!["the", "cat", "sat", "on", "the", "mat", "the", "cat"];

    let mut counts: HashMap<&str, usize> = HashMap::new();
    for word in &words {
        counts
            .entry(word)
            .and_modify(|c| *c += 1)
            .or_insert(1);
    }

    assert_eq!(counts["the"], 3);
    assert_eq!(counts["cat"], 2);
    assert_eq!(counts["sat"], 1);
}

/// Find the most frequent element
fn most_frequent() {
    let items = vec![1, 2, 3, 2, 1, 2, 3, 2, 2];

    let mut counts: HashMap<i32, usize> = HashMap::new();
    for &x in &items {
        *counts.entry(x).or_insert(0) += 1;
    }

    let (most, count) = counts
        .iter()
        .max_by_key(|&(_, &v)| v)
        .map(|(&k, &v)| (k, v))
        .unwrap();

    assert_eq!(most, 2);
    assert_eq!(count, 5);
}

/// Frequency counting with iterators (functional style)
fn functional_counting() {
    let data = vec![1, 1, 2, 3, 3, 3];

    let counts: HashMap<i32, usize> = data.iter().fold(HashMap::new(), |mut acc, &x| {
        *acc.entry(x).or_insert(0) += 1;
        acc
    });

    assert_eq!(counts[&1], 2);
    assert_eq!(counts[&3], 3);
}

fn main() {
    char_frequency();
    word_frequency();
    most_frequent();
    functional_counting();
    println!("โœ“ All tests passed");
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_char_frequency() { char_frequency(); }

    #[test]
    fn test_word_frequency() { word_frequency(); }

    #[test]
    fn test_most_frequent() { most_frequent(); }

    #[test]
    fn test_functional_counting() { functional_counting(); }

    #[test]
    fn test_empty_input() {
        let empty: Vec<i32> = vec![];
        let counts: HashMap<i32, usize> = empty.iter().fold(HashMap::new(), |mut acc, &x| {
            *acc.entry(x).or_insert(0) += 1;
            acc
        });
        assert!(counts.is_empty());
    }
}
(* 1031: Count Frequencies *)
(* Count occurrences of each element *)

module CharMap = Map.Make(Char)
module StringMap = Map.Make(String)

(* Approach 1: Character frequency counting *)
let char_frequency () =
  let text = "abracadabra" in
  let counts = String.fold_left (fun acc ch ->
    let n = match CharMap.find_opt ch acc with
      | Some n -> n
      | None -> 0
    in
    CharMap.add ch (n + 1) acc
  ) CharMap.empty text in
  assert (CharMap.find 'a' counts = 5);
  assert (CharMap.find 'b' counts = 2);
  assert (CharMap.find 'r' counts = 2);
  assert (CharMap.find 'c' counts = 1);
  assert (CharMap.find 'd' counts = 1)

(* Approach 2: Word frequency *)
let word_frequency () =
  let words = ["the"; "cat"; "sat"; "on"; "the"; "mat"; "the"; "cat"] in
  let counts = List.fold_left (fun acc w ->
    let n = match StringMap.find_opt w acc with
      | Some n -> n
      | None -> 0
    in
    StringMap.add w (n + 1) acc
  ) StringMap.empty words in
  assert (StringMap.find "the" counts = 3);
  assert (StringMap.find "cat" counts = 2);
  assert (StringMap.find "sat" counts = 1)

(* Approach 3: Most frequent element *)
let most_frequent () =
  let items = [1; 2; 3; 2; 1; 2; 3; 2; 2] in
  module IntMap = Map.Make(Int) in
  let counts = List.fold_left (fun acc x ->
    let n = match IntMap.find_opt x acc with
      | Some n -> n
      | None -> 0
    in
    IntMap.add x (n + 1) acc
  ) IntMap.empty items in
  let (most, count) = IntMap.fold (fun k v (mk, mv) ->
    if v > mv then (k, v) else (mk, mv)
  ) counts (0, 0) in
  assert (most = 2);
  assert (count = 5)

let () =
  char_frequency ();
  word_frequency ();
  most_frequent ();
  Printf.printf "โœ“ All tests passed\n"

๐Ÿ“Š Detailed Comparison

Count Frequencies โ€” Comparison

Core Insight

Frequency counting is the "hello world" of the Entry API. Both languages use a fold/loop to accumulate counts, but Rust's entry pattern compresses the check-then-update into a single dereference-and-increment.

OCaml Approach

  • `find_opt` + default 0 + `add` with incremented value
  • Pattern: `let n = match find_opt k m with Some n -> n | None -> 0 in add k (n+1) m`
  • `fold` to find max element
  • Each update creates a new map node (immutable)

Rust Approach

  • `*entry(k).or_insert(0) += 1` โ€” one line
  • Alternative: `entry(k).and_modify(|c| *c += 1).or_insert(1)` โ€” more explicit
  • `max_by_key` on iterator to find most frequent
  • Functional style via `iter().fold()` also works

Comparison Table

AspectOCamlRust
Count idiom`find_opt` + match + `add``*entry(k).or_insert(0) += 1`
Lines per count41
Max element`fold` with comparison`iter().max_by_key()`
AllocationNew map node per updateIn-place mutation
StyleAlways functionalImperative or functional