Loading page…
Rust walkthroughs
Loading page…
The itertools crate extends Rust's standard iterator functionality with powerful combinator methods. It provides adapter methods like interleave(), cartesian_product(), unique(), group_by(), and many more that make complex iteration patterns clean and readable. The crate is designed to work seamlessly with standard iterators—just add use itertools::Itertools; to unlock all methods. It's essential for data processing pipelines, algorithm implementations, and any code that works with sequences.
Key concepts:
Itertools adds methods to all iteratorscollect_vec(), collect_tuple() for convenient collection# Cargo.toml
[dependencies]
itertools = "0.13"use itertools::Itertools;
fn main() {
let numbers = vec![1, 2, 3, 4, 5];
// Unique pairs
let pairs: Vec<_> = numbers.iter().tuple_combinations::<(_,_)>().collect();
println!("Pairs: {:?}", pairs);
}use itertools::Itertools;
fn main() {
let numbers = vec![1, 2, 3, 4, 5];
// Easy collection
let doubled: Vec<i32> = numbers.iter().map(|x| x * 2).collect();
println!("Doubled: {:?}", doubled);
// Collect into tuple
let (first, second): (i32, i32) = numbers.iter().take(2).copied().collect_tuple().unwrap();
println!("First two: {}, {}", first, second);
// Join elements
let joined = numbers.iter().join(", ");
println!("Joined: {}", joined);
}use itertools::Itertools;
fn main() {
let numbers = vec![1, 2, 2, 3, 3, 3, 4, 4, 4, 4];
// Get unique elements (preserves order)
let unique: Vec<_> = numbers.iter().unique().collect();
println!("Unique: {:?}", unique);
// Unique by key
let items = vec![("a", 1), ("b", 2), ("a", 3), ("b", 4)];
let unique_by_key: Vec<_> = items.iter().unique_by(|(key, _)| key).collect();
println!("Unique by key: {:?}", unique_by_key);
// Count occurrences
let counts: Vec<_> = numbers.iter().counts().into_iter().collect();
println!("Counts: {:?}", counts);
}use itertools::Itertools;
fn main() {
let numbers = vec![1, 1, 2, 2, 2, 3, 3, 4, 4, 4, 4];
// Group consecutive identical elements
let groups: Vec<Vec<_>> = numbers.iter().group_by(|&x| x).into_iter()
.map(|(key, group)| group.collect())
.collect();
println!("Groups: {:?}", groups);
// Group by property
let items = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let even_odd: Vec<_> = items.iter()
.group_by(|&x| x % 2)
.into_iter()
.map(|(key, group)| (key, group.copied().collect::<Vec<_>>()))
.collect();
println!("Even: {:?}", even_odd.iter().find(|(k, _)| *k == 0).unwrap().1);
println!("Odd: {:?}", even_odd.iter().find(|(k, _)| *k == 1).unwrap().1);
}use itertools::Itertools;
fn main() {
let numbers = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// Fixed-size chunks
let chunks: Vec<Vec<_>> = numbers.iter().chunks(3).into_iter()
.map(|chunk| chunk.collect())
.collect();
println!("Chunks of 3: {:?}", chunks);
// Batching with custom sizes
let mut batch = numbers.iter().batching(|it| {
let mut batch = Vec::new();
while batch.len() < 3 {
match it.next() {
Some(x) => batch.push(x),
None => break,
}
}
if batch.is_empty() { None } else { Some(batch) }
});
while let Some(b) = batch.next() {
println!("Batch: {:?}", b);
}
}use itertools::Itertools;
fn main() {
let letters = vec!['a', 'b'];
let numbers = vec![1, 2, 3];
// Cartesian product
let pairs: Vec<_> = letters.iter().cartesian_product(numbers.iter()).collect();
println!("Pairs: {:?}", pairs);
// Multiple iterators product
let colors = vec!["red", "blue"];
let sizes = vec!["S", "M", "L"];
let products = vec!["shirt", "pants"];
let combinations: Vec<_> = colors.iter()
.cartesian_product(sizes.iter())
.cartesian_product(products.iter())
.collect();
println!("Combinations: {:?}", combinations);
}use itertools::Itertools;
fn main() {
let letters = vec!['a', 'b', 'c', 'd'];
// Combinations of size n (order doesn't matter)
println!("Combinations of 2:");
for combo in letters.iter().combinations(2) {
println!(" {:?}", combo);
}
// Combinations with replacement
println!("\nCombinations with replacement:");
for combo in letters.iter().combinations_with_replacement(2) {
println!(" {:?}", combo);
}
// Permutations of size n (order matters)
println!("\nPermutations of 2:");
for perm in letters.iter().permutations(2) {
println!(" {:?}", perm);
}
// All permutations
println!("\nAll permutations:");
for perm in letters.iter().permutations(letters.len()) {
println!(" {:?}", perm);
}
}use itertools::Itertools;
fn main() {
let numbers = vec![1, 2, 3, 4];
// All unique pairs
println!("Pairs:");
for (a, b) in numbers.iter().tuple_combinations() {
println!(" ({}, {})", a, b);
}
// All unique triples
let letters = vec!['a', 'b', 'c', 'd'];
println!("\nTriples:");
for (a, b, c) in letters.iter().tuple_combinations() {
println!(" ({}, {}, {})", a, b, c);
}
// Windows as tuples
println!("\nWindows (consecutive pairs):");
for (prev, curr) in numbers.iter().tuple_windows() {
println!(" {} -> {}", prev, curr);
}
}use itertools::Itertools;
fn main() {
let a = vec![1, 3, 5, 7];
let b = vec![2, 4, 6, 8];
// Interleave: alternate elements
let interleaved: Vec<_> = a.iter().interleave(b.iter()).collect();
println!("Interleaved: {:?}", interleaved);
// Interleave shortest
let short = vec![1, 3];
let long = vec![2, 4, 6, 8];
let interleaved_short: Vec<_> = short.iter().interleave_shortest(long.iter()).collect();
println!("Interleave shortest: {:?}", interleaved_short);
// Zip longest (fills with None if lengths differ)
let zipped: Vec<_> = short.iter().zip_longest(long.iter()).collect();
println!("Zip longest: {:?}", zipped);
// Zip with custom fill
let zipped_fill: Vec<_> = short.iter().zip_eq(long.iter()).collect();
println!("Zip eq (requires same length): {:?}", zipped_fill);
}use itertools::Itertools;
fn main() {
let a = vec![1, 3, 5, 7, 9];
let b = vec![2, 4, 6, 8, 10];
// Merge two sorted iterators
let merged: Vec<_> = a.iter().merge(b.iter()).copied().collect();
println!("Merged: {:?}", merged);
// Merge multiple sorted iterators
let c = vec![0, 11];
let d = vec![5, 6];
let merged_all: Vec<_> = a.iter()
.merge(b.iter())
.merge(c.iter())
.copied()
.collect();
println!("Merged all: {:?}", merged_all);
// Merge by key
let left = vec![(1, "a"), (3, "c"), (5, "e")];
let right = vec![(2, "b"), (4, "d"), (6, "f")];
let merged_by: Vec<_> = left.iter().merge_by(right.iter(), |l, r| l.0 < r.0).collect();
println!("Merged by key: {:?}", merged_by);
}use itertools::Itertools;
fn main() {
let numbers = vec![1, 2, 3, 4, 5];
// Fold results
let sum: Vec<_> = numbers.iter().copied().fold_results(0, |acc, x| acc + x).collect();
println!("Sum: {:?}", sum);
// Tree fold (balanced folding)
let tree_sum = numbers.iter().copied().tree_fold(|a, b| a + b);
println!("Tree fold sum: {:?}", tree_sum);
// Reduce with state
let result = numbers.iter().copied().reduce(|acc, x| acc + x);
println!("Reduce sum: {:?}", result);
// Scan and fold
let running_sum: Vec<_> = numbers.iter().scan(0, |state, &x| {
*state += x;
Some(*state)
}).collect();
println!("Running sum: {:?}", running_sum);
}use itertools::Itertools;
fn main() {
let numbers = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// Take while predicate is true, then continue
let (taken, rest): (Vec<_>, Vec<_>) = numbers.iter()
.copied()
.partition(|&x| x < 5);
println!("Taken (<5): {:?}", taken);
println!("Rest: {:?}", rest);
// Take until predicate is true (exclusive)
let taken: Vec<_> = numbers.iter().copied().take_while(|&x| x < 5).collect();
println!("Take while <5: {:?}", taken);
// Skip while predicate is true
let skipped: Vec<_> = numbers.iter().copied().skip_while(|&x| x < 5).collect();
println!("Skip while <5: {:?}", skipped);
}use itertools::Itertools;
fn main() {
let numbers = vec![10, 20, 30, 40, 50];
// Enumerate starting from 1
for (i, val) in numbers.iter().enumerate().map(|(i, v)| (i + 1, v)) {
println!("{}. {}", i, val);
}
// Find position
let pos = numbers.iter().position(|&x| x == 30);
println!("Position of 30: {:?}", pos);
// Positions where predicate is true
let positions: Vec<_> = numbers.iter()
.positions(|&x| x > 25)
.collect();
println!("Positions > 25: {:?}", positions);
// With index
for (idx, val) in numbers.iter().with_position() {
match idx {
itertools::Position::First => println!("First: {}", val),
itertools::Position::Middle => println!("Middle: {}", val),
itertools::Position::Last => println!("Last: {}", val),
itertools::Position::Only => println!("Only: {}", val),
}
}
}use itertools::Itertools;
fn main() {
let nested = vec![vec![1, 2], vec![3, 4], vec![5, 6]];
// Flatten (same as .flatten())
let flat: Vec<_> = nested.iter().flatten().copied().collect();
println!("Flattened: {:?}", flat);
// Flatten one level
let flat2: Vec<_> = nested.iter().concat();
println!("Concatenated: {:?}", flat2);
// Flatten Option
let options = vec![Some(1), None, Some(2), None, Some(3)];
let flattened: Vec<_> = options.into_iter().flatten().collect();
println!("Flattened Options: {:?}", flattened);
// Flatten Result
let results: Vec<Result<i32, &str>> = vec![Ok(1), Err("error"), Ok(2)];
let ok_values: Vec<_> = results.into_iter().filter_map(|r| r.ok()).collect();
println!("Ok values: {:?}", ok_values);
}use itertools::Itertools;
fn main() {
let numbers = vec![3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5];
// Min and max
println!("Min: {:?}", numbers.iter().min());
println!("Max: {:?}", numbers.iter().max());
// Min/Max by key
let items = vec![("a", 3), ("b", 1), ("c", 4), ("d", 1)];
let min_by_val = items.iter().min_by_key(|(_, v)| v);
println!("Min by value: {:?}", min_by_val);
// Min/Max position
let min_pos = numbers.iter().position_min();
let max_pos = numbers.iter().position_max();
println!("Min position: {:?}, Max position: {:?}", min_pos, max_pos);
// Multiple min/max
let minmax = numbers.iter().minmax();
println!("Min/Max: {:?}", minmax);
// All extremes
let maxes: Vec<_> = items.iter().max_set_by_key(|(_, v)| *v);
println!("All max items: {:?}", maxes);
}use itertools::Itertools;
fn main() {
let numbers = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// Partition into two collections
let (evens, odds): (Vec<_>, Vec<_>) = numbers.iter()
.copied()
.partition(|&x| x % 2 == 0);
println!("Evens: {:?}", evens);
println!("Odds: {:?}", odds);
// Partition in place
let mut numbers2 = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let split_idx = itertools::partition(&mut numbers2, |&x| x % 2 == 0);
println!("Partitioned: {:?}", numbers2);
println!("Split at index: {}", split_idx);
}use itertools::Itertools;
fn main() {
let old = vec![1, 2, 3, 4, 5];
let new = vec![1, 2, 4, 5, 6];
// Compare sequences
for diff in old.iter().diff(new.iter()) {
println!("Diff: {:?}", diff);
}
// Compare with positions
let old2 = vec!['a', 'b', 'c', 'd'];
let new2 = vec!['a', 'b', 'x', 'd'];
for change in old2.iter().diff_with(new2.iter()) {
match change {
itertools::Diff::FirstMismatch(pos, old, new) => {
println!("Mismatch at {}: {:?} vs {:?}", pos, old, new);
}
_ => {}
}
}
// Longest common prefix
let common_len = old.iter().zip(new.iter()).take_while(|(a, b)| a == b).count();
println!("Common prefix length: {}", common_len);
}use itertools::Itertools;
fn main() {
let numbers = vec![1, 2, 3, 4, 5];
// Next or default
let mut iter = numbers.iter().copied();
println!("Next: {:?}", iter.next());
println!("Next or 0: {:?}", iter.next_or(0));
// Iterate with done flag
for (done, val) in numbers.iter().with_done() {
if done {
println!("Last: {}", val);
} else {
println!("Not last: {}", val);
}
}
// Cycle forever
let cycled: Vec<_> = numbers.iter().cycle().take(10).copied().collect();
println!("Cycled (10): {:?}", cycled);
}use itertools::Itertools;
struct Record {
id: u32,
category: String,
value: f64,
}
fn main() {
let records = vec![
Record { id: 1, category: "A".to_string(), value: 10.0 },
Record { id: 2, category: "B".to_string(), value: 20.0 },
Record { id: 3, category: "A".to_string(), value: 15.0 },
Record { id: 4, category: "B".to_string(), value: 25.0 },
Record { id: 5, category: "A".to_string(), value: 12.0 },
];
// Group by category and compute statistics
let stats: Vec<_> = records.iter()
.group_by(|r| r.category.as_str())
.into_iter()
.map(|(category, group)| {
let values: Vec<_> = group.map(|r| r.value).collect();
let sum: f64 = values.iter().sum();
let count = values.len();
let avg = sum / count as f64;
(category, count, sum, avg)
})
.collect();
println!("Category Statistics:");
for (cat, count, sum, avg) in stats {
println!(" {}: count={}, sum={:.1}, avg={:.1}", cat, count, sum, avg);
}
}use itertools::Itertools;
use std::collections::HashMap;
fn main() {
let words = vec!["listen", "silent", "enlist", "hello", "world", "dlrow", "evil", "live", "vile"];
// Group words by their sorted character multiset
let anagrams: HashMap<Vec<char>, Vec<&str>> = words.iter()
.map(|&word| {
let mut chars: Vec<char> = word.chars().collect();
chars.sort();
(chars, word)
})
.into_group_map();
println!("Anagrams:");
for (_, group) in anagrams.iter().filter(|(_, g)| g.len() > 1) {
println!(" {:?}", group);
}
}use itertools::Itertools;
fn main() {
// Generate all possible test inputs
let operations = vec!["add", "remove", "query"];
let keys = vec!["user_1", "user_2", "user_3"];
let values = vec![100, 200, 300];
let test_cases: Vec<_> = operations.iter()
.cartesian_product(keys.iter())
.cartesian_product(values.iter())
.map(|((op, key), value)| format!("{}({}, {})", op, key, value))
.collect();
println!("Test cases:");
for tc in test_cases.iter().take(10) {
println!(" {}", tc);
}
println!("... ({} total)", test_cases.len());
}use itertools::Itertools;
fn main() {
let prices = vec![100.0, 102.0, 101.0, 105.0, 103.0, 107.0, 106.0, 110.0];
// Calculate moving average
println!("Moving averages (window=3):");
for (i, window) in prices.iter().tuple_windows::<(_,_,_)>().enumerate() {
let (a, b, c) = window;
let avg = (a + b + c) / 3.0;
println!(" Day {}-{}: avg = {:.2}", i + 1, i + 3, avg);
}
// Detect trends
println!("\nTrend changes:");
for ((prev, curr), next) in prices.iter().tuple_windows::<(_,_,_)>() {
if prev < curr && curr > next {
println!(" Peak at {:.2}", curr);
} else if prev > curr && curr < next {
println!(" Valley at {:.2}", curr);
}
}
}use itertools::Itertools;
fn parse_version(version: &str) -> Vec<u32> {
version.split('.').filter_map(|s| s.parse().ok()).collect()
}
fn main() {
let versions = vec!["1.0.0", "1.2.0", "1.2.1", "2.0.0", "2.0.1"];
// Find version between two versions
let target = "1.2.0";
let target_ver = parse_version(target);
let (before, after): (Vec<_>, Vec<_>) = versions.iter()
.partition(|v| {
let v_parsed = parse_version(v);
v_parsed < target_ver
});
println!("Before {}: {:?}", target, before);
println!("After or equal {}: {:?}", target, after);
}use itertools::Itertools;
struct User {
id: u32,
name: String,
}
struct Order {
user_id: u32,
product: String,
amount: f64,
}
fn main() {
let users = vec![
User { id: 1, name: "Alice".to_string() },
User { id: 2, name: "Bob".to_string() },
User { id: 3, name: "Charlie".to_string() },
];
let orders = vec![
Order { user_id: 1, product: "Book".to_string(), amount: 25.0 },
Order { user_id: 2, product: "Laptop".to_string(), amount: 1000.0 },
Order { user_id: 1, product: "Pen".to_string(), amount: 5.0 },
Order { user_id: 3, product: "Notebook".to_string(), amount: 15.0 },
];
// Group orders by user
let orders_by_user = orders.iter()
.group_by(|o| o.user_id)
.into_iter()
.map(|(user_id, group)| {
(user_id, group.collect::<Vec<_>>())
})
.collect::<std::collections::HashMap<_, _>>();
// Print user order summary
for user in users {
if let Some(user_orders) = orders_by_user.get(&user.id) {
let total: f64 = user_orders.iter().map(|o| o.amount).sum();
println!("{}: {} orders, ${:.2} total",
user.name, user_orders.len(), total);
}
}
}use itertools::Itertools;
#[derive(Debug, Clone)]
enum Token {
Number(i32),
Plus,
Minus,
Multiply,
Divide,
}
fn tokenize(input: &str) -> Vec<Token> {
input.chars()
.filter(|c| !c.is_whitespace())
.batching(|it| {
let c = it.next()?;
Some(match c {
'+' => Token::Plus,
'-' => Token::Minus,
'*' => Token::Multiply,
'/' => Token::Divide,
n if n.is_ascii_digit() => {
let mut num = n.to_string();
while let Some(&c) = it.peek() {
if c.is_ascii_digit() {
num.push(it.next()?);
} else {
break;
}
}
Token::Number(num.parse().unwrap())
}
_ => return None,
})
})
.collect()
}
fn main() {
let input = "12 + 34 * 2 - 5";
let tokens = tokenize(input);
println!("Tokens: {:?}", tokens);
}use itertools::Itertools;
#[derive(Debug, Clone)]
struct LogLine {
timestamp: String,
level: String,
message: String,
}
fn main() {
let logs = vec![
LogLine { timestamp: "2024-01-01T10:00:00".into(), level: "INFO".into(), message: "Started".into() },
LogLine { timestamp: "2024-01-01T10:00:01".into(), level: "INFO".into(), message: "Processing".into() },
LogLine { timestamp: "2024-01-01T10:00:02".into(), level: "INFO".into(), message: "Processing".into() },
LogLine { timestamp: "2024-01-01T10:00:03".into(), level: "WARN".into(), message: "Slow query".into() },
LogLine { timestamp: "2024-01-01T10:00:04".into(), level: "INFO".into(), message: "Processing".into() },
LogLine { timestamp: "2024-01-01T10:00:05".into(), level: "ERROR".into(), message: "Failed".into() },
];
// Dedupe consecutive identical messages
let deduped: Vec<_> = logs.iter()
.coalesce(|a, b| {
if a.message == b.message {
Ok(a.clone())
} else {
Err((a.clone(), b.clone()))
}
})
.collect();
println!("Deduplicated logs:");
for log in deduped {
println!(" [{}] {} - {}", log.timestamp, log.level, log.message);
}
}use itertools::Itertools;
fn main() {
let large: Vec<i32> = (0..1000000).collect();
// Use specialized methods when available
// concat is faster than flatten + collect
let nested: Vec<Vec<i32>> = vec![vec![1, 2], vec![3, 4]];
let flat: Vec<i32> = nested.into_iter().concat();
// Use fold_while for early termination
let result = (0..).fold_while(0, |acc, x| {
if acc > 100 {
itertools::FoldWhile::Done(acc)
} else {
itertools::FoldWhile::Continue(acc + x)
}
});
println!("Fold while result: {:?}", result.into_inner());
// Avoid collect when not needed
let sum: i32 = large.iter()
.copied()
.fold(0, |acc, x| acc + x);
println!("Sum computed without collecting: {}", sum);
}use itertools::Itertools; to access all extension methodsunique() and unique_by() remove duplicates while preserving ordercombinations(n) generates all n-element combinationspermutations(n) generates all n-element permutationscartesian_product() combines every element from two iteratorsinterleave() alternates between two iteratorsgroup_by() groups consecutive elements by a keychunks(n) splits into fixed-size chunksmerge() combines sorted iterators into one sorted iteratorpartition() splits elements into two collectionstuple_windows() creates sliding windows of tuplesminmax() finds both min and max in one passcollect_tuple() collects exactly N elements into a tuplejoin() concatenates elements with a separatorfold_while() allows early termination in a foldwith_position() identifies first/middle/last elementscoalesce() merges consecutive elements