// Example 098: Partition Iterator
// Split into two collections by predicate
// === Approach 1: Basic partition ===
fn split_even_odd(data: &[i32]) -> (Vec<i32>, Vec<i32>) {
data.iter().partition(|&&x| x % 2 == 0)
}
fn split_positive(data: &[i32]) -> (Vec<&i32>, Vec<&i32>) {
data.iter().partition(|&&x| x > 0)
}
// === Approach 2: Multi-way partition ===
fn partition3<T>(data: &[T], p1: impl Fn(&T) -> bool, p2: impl Fn(&T) -> bool) -> (Vec<&T>, Vec<&T>, Vec<&T>) {
let mut a = Vec::new();
let mut b = Vec::new();
let mut c = Vec::new();
for item in data {
if p1(item) { a.push(item); }
else if p2(item) { b.push(item); }
else { c.push(item); }
}
(a, b, c)
}
fn classify_numbers(data: &[i32]) -> (Vec<&i32>, Vec<&i32>, Vec<&i32>) {
partition3(data, |&&x| x < 0, |&&x| x == 0)
}
// === Approach 3: Partition with transformation ===
fn partition_results(data: &[&str]) -> (Vec<i32>, Vec<String>) {
let mut oks = Vec::new();
let mut errs = Vec::new();
for &s in data {
match s.parse::<i32>() {
Ok(n) => oks.push(n),
Err(e) => errs.push(format!("{}: {}", s, e)),
}
}
(oks, errs)
}
fn validate_ages(ages: &[i32]) -> (Vec<i32>, Vec<i32>) {
ages.iter().partition(|&&a| (0..=150).contains(&a))
}
// split_at equivalent
fn split_at_first<T>(data: &[T], pred: impl Fn(&T) -> bool) -> (&[T], &[T]) {
match data.iter().position(|x| pred(x)) {
Some(pos) => (&data[..pos], &data[pos..]),
None => (data, &[]),
}
}
// Partition into HashMap buckets
use std::collections::HashMap;
fn bucket_by<T, K: std::hash::Hash + Eq>(data: &[T], key: impl Fn(&T) -> K) -> HashMap<K, Vec<&T>> {
let mut map: HashMap<K, Vec<&T>> = HashMap::new();
for item in data {
map.entry(key(item)).or_default().push(item);
}
map
}
fn main() {
let (evens, odds) = split_even_odd(&[1,2,3,4,5,6]);
println!("Evens: {:?}, Odds: {:?}", evens, odds);
let (neg, zero, pos) = classify_numbers(&[-2, 0, 3, -1, 0, 5]);
println!("Neg: {:?}, Zero: {:?}, Pos: {:?}", neg, zero, pos);
let (oks, errs) = partition_results(&["1", "abc", "3", "xyz"]);
println!("Parsed: {:?}, Errors: {:?}", oks, errs);
let (before, after) = split_at_first(&[1,2,3,4,5], |x| *x > 3);
println!("Before: {:?}, After: {:?}", before, after);
let buckets = bucket_by(&[1,2,3,4,5,6,7,8,9], |x| *x % 3);
println!("Buckets: {:?}", buckets);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_split_even_odd() {
let (evens, odds) = split_even_odd(&[1,2,3,4,5,6]);
assert_eq!(evens, vec![2,4,6]);
assert_eq!(odds, vec![1,3,5]);
}
#[test]
fn test_classify_numbers() {
let (neg, zero, pos) = classify_numbers(&[-2, 0, 3, -1, 0, 5]);
assert_eq!(neg, vec![&-2, &-1]);
assert_eq!(zero, vec![&0, &0]);
assert_eq!(pos, vec![&3, &5]);
}
#[test]
fn test_partition_results() {
let (oks, errs) = partition_results(&["1", "abc", "3"]);
assert_eq!(oks, vec![1, 3]);
assert_eq!(errs.len(), 1);
}
#[test]
fn test_validate_ages() {
let (valid, invalid) = validate_ages(&[25, -5, 200, 30, 0]);
assert_eq!(valid, vec![25, 30, 0]);
assert_eq!(invalid, vec![-5, 200]);
}
#[test]
fn test_split_at_first() {
let (before, after) = split_at_first(&[1,2,3,4,5], |x| *x > 3);
assert_eq!(before, &[1,2,3]);
assert_eq!(after, &[4,5]);
}
#[test]
fn test_split_at_none() {
let (before, after) = split_at_first(&[1,2,3], |x| *x > 10);
assert_eq!(before, &[1,2,3]);
assert!(after.is_empty());
}
#[test]
fn test_bucket_by() {
let buckets = bucket_by(&["hello", "hi", "world", "wow"], |s| s.chars().next().unwrap());
assert_eq!(buckets[&'h'].len(), 2);
assert_eq!(buckets[&'w'].len(), 2);
}
}
(* Example 098: Partition Iterator *)
(* Split into two collections by predicate *)
(* Approach 1: List.partition *)
let split_even_odd lst =
List.partition (fun x -> x mod 2 = 0) lst
let split_positive lst =
List.partition (fun x -> x > 0) lst
(* Approach 2: Multi-way partition *)
let partition3 p1 p2 lst =
List.fold_right (fun x (a, b, c) ->
if p1 x then (x :: a, b, c)
else if p2 x then (a, x :: b, c)
else (a, b, x :: c)
) lst ([], [], [])
let classify_numbers lst =
partition3 (fun x -> x < 0) (fun x -> x = 0) lst
(* Approach 3: Partition with transformation *)
let partition_map f lst =
List.fold_right (fun x (lefts, rights) ->
match f x with
| Either.Left l -> (l :: lefts, rights)
| Either.Right r -> (lefts, r :: rights)
) lst ([], [])
let validate_ages ages =
let valid, invalid = List.partition (fun a -> a >= 0 && a <= 150) ages in
(valid, invalid)
let split_at_first pred lst =
let rec aux acc = function
| [] -> (List.rev acc, [])
| x :: rest when pred x -> (List.rev acc, x :: rest)
| x :: rest -> aux (x :: acc) rest
in
aux [] lst
(* Tests *)
let () =
let (evens, odds) = split_even_odd [1;2;3;4;5;6] in
assert (evens = [2;4;6]);
assert (odds = [1;3;5]);
let (pos, neg) = split_positive [3; -1; 4; -5; 0] in
assert (pos = [3; 4]);
assert (neg = [-1; -5; 0]);
let (neg, zero, pos) = classify_numbers [-2; 0; 3; -1; 0; 5] in
assert (neg = [-2; -1]);
assert (zero = [0; 0]);
assert (pos = [3; 5]);
let (valid, invalid) = validate_ages [25; -5; 200; 30; 0] in
assert (valid = [25; 30; 0]);
assert (invalid = [-5; 200]);
let (before, after) = split_at_first (fun x -> x > 3) [1;2;3;4;5] in
assert (before = [1;2;3]);
assert (after = [4;5]);
Printf.printf "โ All tests passed\n"