๐Ÿฆ€ Functional Rust
๐ŸŽฌ How Rust Iterators Work Lazy evaluation, chaining, collect(), and zero-cost abstractions.
๐Ÿ“ Text version (for readers / accessibility)

โ€ข Iterators are lazy โ€” .map(), .filter(), .take() build a chain but do no work until consumed

โ€ข .collect() triggers evaluation, transforming the chain into a Vec, HashMap, or other collection

โ€ข Zero-cost abstraction: iterator chains compile to the same machine code as hand-written loops

โ€ข .iter() borrows, .into_iter() consumes, .iter_mut() borrows mutably

โ€ข Chaining replaces nested loops with a readable, composable pipeline

276: Iterator min_by() and max_by()

Difficulty: 2 Level: Intermediate Find the minimum or maximum element using a custom comparator closure that returns `Ordering` โ€” essential for floats, complex structs, and multi-key sorts.

The Problem This Solves

`min()` and `max()` require `Ord` โ€” a total ordering. But `f64` doesn't implement `Ord` because `NaN != NaN`. And sometimes you want to compare structs by multiple fields: "shortest string, then alphabetical as tiebreaker." You can't express that with `min_by_key` alone. `min_by` and `max_by` give you a full comparator closure: `Fn(&A, &A) -> Ordering`. This unlocks any comparison logic you can express: float comparisons with NaN handling, multi-field sorting, distance calculations, reversed ordering (finding the max via `min_by` with a reversed comparator). The `Ordering` type (`Less`, `Equal`, `Greater`) combined with `.then_with()` enables clean multi-key comparisons without custom `Ord` implementations on your types.

The Intuition

Like `min()` and `max()`, but instead of requiring `Ord`, you provide the comparison function yourself โ€” full control over what "less than" means.

How It Works in Rust

use std::cmp::Ordering;

// f64 min/max โ€” handle NaN by falling back to Equal
let floats = [3.14f64, 1.41, 2.71, 0.57];
let min_f = floats.iter().copied()
 .min_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Equal));
// โ†’ Some(0.57)

// Multi-key: shortest first, then alphabetical as tiebreaker
let words = ["banana", "apple", "fig", "kiwi"];
let min_word = words.iter().min_by(|a, b| {
 a.len().cmp(&b.len())         // primary: length
  .then_with(|| a.cmp(b))      // tiebreaker: alphabetical
});
// โ†’ Some("fig")

// Closest point to origin โ€” computed key, not a simple field
struct Point { x: f64, y: f64 }
let closest = points.iter().min_by(|a, b| {
 let da = (a.x*a.x + a.y*a.y).sqrt();
 let db = (b.x*b.x + b.y*b.y).sqrt();
 da.partial_cmp(&db).unwrap_or(Ordering::Equal)
});

// Trick: find max using min_by with reversed comparator
let max_via_min = nums.iter().min_by(|a, b| b.cmp(a));

What This Unlocks

Key Differences

ConceptOCamlRust
Custom min`List.fold_left` with custom compare`.min_by(\a, b\...)`
Comparator signature`'a -> 'a -> int` (-1/0/1)`Fn(&A, &A) -> Ordering`
Float comparison`Float.compare` (works)`partial_cmp().unwrap_or(Equal)`
Multi-keyManual chaining with `if``.then_with(\\...)` on Ordering
Reverse orderSwap args in comparatorSwap args in comparator (`b.cmp(a)`)
//! 276. Custom comparison min_by() and max_by()
//!
//! `min_by(cmp)` and `max_by(cmp)` take a `Fn(&A, &A) -> Ordering` comparator.

use std::cmp::Ordering;

fn main() {
    let floats = [3.14f64, 1.41, 2.71, 1.73, 0.57];
    let min_f = floats.iter().copied()
        .min_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Equal));
    let max_f = floats.iter().copied()
        .max_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Equal));
    println!("Min float: {:?}", min_f);
    println!("Max float: {:?}", max_f);

    // Multi-key: min by length, then alphabetical
    let words = ["banana", "apple", "fig", "kiwi", "cherry"];
    let min_word = words.iter().min_by(|a, b| {
        a.len().cmp(&b.len()).then_with(|| a.cmp(b))
    });
    println!("Min by len then alpha: {:?}", min_word);

    // Reverse: find max using min_by with reversed comparator
    let nums = [5i32, 2, 8, 1, 9, 3];
    let max_via_reversed = nums.iter().min_by(|a, b| b.cmp(a));
    println!("Max via reversed min_by: {:?}", max_via_reversed);

    // Closest point to origin
    #[derive(Debug)]
    struct Point { x: f64, y: f64 }
    let points = [Point{x:1.0,y:3.0}, Point{x:2.0,y:1.0}, Point{x:0.5,y:2.0}];
    let closest = points.iter().min_by(|a, b| {
        let da = (a.x*a.x + a.y*a.y).sqrt();
        let db = (b.x*b.x + b.y*b.y).sqrt();
        da.partial_cmp(&db).unwrap_or(Ordering::Equal)
    });
    println!("Closest to origin: {:?}", closest);
}

#[cfg(test)]
mod tests {
    use std::cmp::Ordering;

    #[test]
    fn test_min_by_float() {
        let floats = [3.0f64, 1.0, 2.0];
        let min = floats.iter().copied()
            .min_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Equal));
        assert_eq!(min, Some(1.0));
    }

    #[test]
    fn test_max_by_reversed() {
        let nums = [1i32, 5, 3, 2, 4];
        let max = nums.iter().min_by(|a, b| b.cmp(a));
        assert_eq!(max, Some(&5));
    }

    #[test]
    fn test_min_by_multi_key() {
        let words = ["bb", "aa", "c"];
        let min = words.iter().min_by(|a, b| {
            a.len().cmp(&b.len()).then_with(|| a.cmp(b))
        });
        assert_eq!(min, Some(&"c"));
    }
}
(* 276. Custom comparison min_by() and max_by() - OCaml *)

let min_by cmp lst =
  match lst with
  | [] -> None
  | x :: xs -> Some (List.fold_left (fun a y -> if cmp y a < 0 then y else a) x xs)

let max_by cmp lst =
  match lst with
  | [] -> None
  | x :: xs -> Some (List.fold_left (fun a y -> if cmp y a > 0 then y else a) x xs)

let () =
  let floats = [3.14; 1.41; 2.71; 1.73; 0.57] in
  (match min_by Float.compare floats with Some v -> Printf.printf "Min: %.2f\n" v | None -> ());
  (match max_by Float.compare floats with Some v -> Printf.printf "Max: %.2f\n" v | None -> ());

  let words = ["banana"; "apple"; "fig"; "kiwi"; "cherry"] in
  let cmp_len a b =
    let c = compare (String.length a) (String.length b) in
    if c <> 0 then c else String.compare a b
  in
  (match min_by cmp_len words with Some w -> Printf.printf "Min by len+alpha: %s\n" w | None -> ());

  let nums = [5; 2; 8; 1; 9; 3] in
  (match min_by (fun a b -> -compare a b) nums with
  | Some n -> Printf.printf "Max via reversed min: %d\n" n
  | None -> ())