257: Pairing Elements with zip()
Functional Programming
Tutorial
The Problem
Many algorithms operate on two parallel sequences simultaneously: pairing keys with values, computing dot products, correlating time-series data, or combining two streams element-by-element. The naive approach uses index-based access (a[i], b[i]) which is error-prone and requires bounds checking. The zip() combinator solves this by producing pairs (a_i, b_i) lazily, stopping when the shorter source is exhausted — eliminating off-by-one errors entirely.
🎯 Learning Outcomes
zip() pairs elements from two iterators, stopping at the shorter onezip() to build HashMap from key and value iteratorszip() with map() to compute pairwise operations like dot productsCode Example
#![allow(clippy::all)]
//! 257. Pairing elements with zip()
//!
//! `zip()` pairs elements from two iterators, stopping at the shorter one.
#[cfg(test)]
mod tests {
#[test]
fn test_zip_basic() {
let a = [1i32, 2, 3];
let b = ["x", "y", "z"];
let result: Vec<_> = a.iter().zip(b.iter()).collect();
assert_eq!(result.len(), 3);
assert_eq!(result[0], (&1, &"x"));
}
#[test]
fn test_zip_truncates() {
let a = [1i32, 2, 3, 4, 5];
let b = [10i32, 20];
let result: Vec<_> = a.iter().zip(b.iter()).collect();
assert_eq!(result.len(), 2);
}
#[test]
fn test_zip_into_hashmap() {
let keys = vec!["a", "b"];
let vals = vec![1i32, 2];
let map: std::collections::HashMap<_, _> = keys.into_iter().zip(vals).collect();
assert_eq!(map["a"], 1);
assert_eq!(map["b"], 2);
}
}Key Differences
zip() silently truncates to the shorter iterator; OCaml's List.map2 raises Invalid_argument.zip() is lazy; OCaml's List.combine immediately creates a new list.(A, B) which are unnamed; OCaml produces named-field or anonymous tuples identically.Iterator::unzip() as the inverse; OCaml uses List.split.OCaml Approach
OCaml provides List.combine for zipping two lists into a list of pairs, and List.map2 for applying a binary function pairwise. These are strict and raise Invalid_argument on length mismatch, unlike Rust's truncation:
let pairs = List.combine [1;2;3] ["a";"b";"c"]
(* [(1,"a"); (2,"b"); (3,"c")] — strict, raises on unequal lengths *)
let dot = List.fold_left (+) 0 (List.map2 ( * ) a b)
Full Source
#![allow(clippy::all)]
//! 257. Pairing elements with zip()
//!
//! `zip()` pairs elements from two iterators, stopping at the shorter one.
#[cfg(test)]
mod tests {
#[test]
fn test_zip_basic() {
let a = [1i32, 2, 3];
let b = ["x", "y", "z"];
let result: Vec<_> = a.iter().zip(b.iter()).collect();
assert_eq!(result.len(), 3);
assert_eq!(result[0], (&1, &"x"));
}
#[test]
fn test_zip_truncates() {
let a = [1i32, 2, 3, 4, 5];
let b = [10i32, 20];
let result: Vec<_> = a.iter().zip(b.iter()).collect();
assert_eq!(result.len(), 2);
}
#[test]
fn test_zip_into_hashmap() {
let keys = vec!["a", "b"];
let vals = vec![1i32, 2];
let map: std::collections::HashMap<_, _> = keys.into_iter().zip(vals).collect();
assert_eq!(map["a"], 1);
assert_eq!(map["b"], 2);
}
}
✓ Tests
Rust test suite
#[cfg(test)]
mod tests {
#[test]
fn test_zip_basic() {
let a = [1i32, 2, 3];
let b = ["x", "y", "z"];
let result: Vec<_> = a.iter().zip(b.iter()).collect();
assert_eq!(result.len(), 3);
assert_eq!(result[0], (&1, &"x"));
}
#[test]
fn test_zip_truncates() {
let a = [1i32, 2, 3, 4, 5];
let b = [10i32, 20];
let result: Vec<_> = a.iter().zip(b.iter()).collect();
assert_eq!(result.len(), 2);
}
#[test]
fn test_zip_into_hashmap() {
let keys = vec!["a", "b"];
let vals = vec![1i32, 2];
let map: std::collections::HashMap<_, _> = keys.into_iter().zip(vals).collect();
assert_eq!(map["a"], 1);
assert_eq!(map["b"], 2);
}
}
Exercises
zip() and map().dot(a: &[f64], b: &[f64]) -> f64 using zip() and sum().zip() with enumerate() to pair every element with both its index and a label from a separate labels slice.