ExamplesBy LevelBy TopicLearning Paths
351 Intermediate

351: BTreeMap Ordered

Functional Programming

Tutorial

The Problem

HashMap gives O(1) average lookup but no ordering guarantees — iterating a HashMap yields keys in unpredictable order, and range queries are impossible. BTreeMap solves this by storing key-value pairs in a B-tree (Bayer & McCreight, 1972) — a self-balancing tree optimized for block-access patterns. All operations are O(log n) in the worst case, and keys are always iterated in sorted order. BTreeMap is the right tool when you need sorted output, range queries, min_key/max_key in O(log n), or when the number of entries is small enough that cache-friendly sorted data beats hash table overhead.

🎯 Learning Outcomes

  • • Construct a BTreeMap<K, V> from an iterator of pairs
  • • Use .range(from..=to) for efficient range queries without scanning all keys
  • • Access the minimum key with .keys().next() and maximum with .keys().next_back()
  • • Understand that BTreeMap requires K: Ord (total ordering), unlike HashMap's K: Hash + Eq
  • • Recognize when to prefer BTreeMap over HashMap based on access patterns
  • • Use entry() API for conditional insertion with ordered keys
  • Code Example

    #![allow(clippy::all)]
    //! # BTreeMap Ordered
    //! Sorted key-value map with efficient range queries.
    
    use std::collections::BTreeMap;
    
    pub fn sorted_map<K: Ord, V>(pairs: Vec<(K, V)>) -> BTreeMap<K, V> {
        pairs.into_iter().collect()
    }
    
    pub fn range_query<K: Ord + Clone, V: Clone>(
        map: &BTreeMap<K, V>,
        from: &K,
        to: &K,
    ) -> Vec<(K, V)> {
        map.range(from..=to)
            .map(|(k, v)| (k.clone(), v.clone()))
            .collect()
    }
    
    pub fn min_key<K: Clone, V>(map: &BTreeMap<K, V>) -> Option<K> {
        map.keys().next().cloned()
    }
    
    pub fn max_key<K: Clone, V>(map: &BTreeMap<K, V>) -> Option<K> {
        map.keys().next_back().cloned()
    }
    
    #[cfg(test)]
    mod tests {
        use super::*;
    
        #[test]
        fn sorted_iteration() {
            let m = sorted_map(vec![(3, 'c'), (1, 'a'), (2, 'b')]);
            let keys: Vec<_> = m.keys().cloned().collect();
            assert_eq!(keys, vec![1, 2, 3]);
        }
        #[test]
        fn range_works() {
            let m: BTreeMap<i32, i32> = (0..10).map(|i| (i, i * i)).collect();
            let r = range_query(&m, &3, &5);
            assert_eq!(r, vec![(3, 9), (4, 16), (5, 25)]);
        }
        #[test]
        fn min_max_keys() {
            let m = sorted_map(vec![(5, 'e'), (1, 'a'), (9, 'i')]);
            assert_eq!(min_key(&m), Some(1));
            assert_eq!(max_key(&m), Some(9));
        }
    }

    Key Differences

    AspectRust BTreeMapOCaml Map.Make
    Underlying structureB-tree (cache-friendly)AVL tree (balanced binary tree)
    MutabilityIn-place mutationPersistent (immutable, functional)
    Range queryO(log n + k) with .range()O(n) with filter
    Ordering requirementK: Ordcompare function via functor
    Min/maxO(log n) via next()/next_back()O(log n) via min_binding/max_binding

    OCaml Approach

    OCaml's Map.Make functor creates ordered maps parameterized by a comparison module:

    module IntMap = Map.Make(Int)
    
    let sorted_map pairs =
      List.fold_left (fun m (k, v) -> IntMap.add k v m) IntMap.empty pairs
    
    (* Range query: filter (no built-in range iterator) *)
    let range_query m lo hi =
      IntMap.filter (fun k _ -> k >= lo && k <= hi) m
      |> IntMap.to_seq |> List.of_seq
    
    let min_key m = fst (IntMap.min_binding m)
    let max_key m = fst (IntMap.max_binding m)
    

    OCaml's Map is a purely functional AVL tree — all operations return new maps (persistent). Rust's BTreeMap is imperative with in-place modification. OCaml lacks a built-in range query iterator; Map.filter scans all entries (O(n)) unless you use a custom fold.

    Full Source

    #![allow(clippy::all)]
    //! # BTreeMap Ordered
    //! Sorted key-value map with efficient range queries.
    
    use std::collections::BTreeMap;
    
    pub fn sorted_map<K: Ord, V>(pairs: Vec<(K, V)>) -> BTreeMap<K, V> {
        pairs.into_iter().collect()
    }
    
    pub fn range_query<K: Ord + Clone, V: Clone>(
        map: &BTreeMap<K, V>,
        from: &K,
        to: &K,
    ) -> Vec<(K, V)> {
        map.range(from..=to)
            .map(|(k, v)| (k.clone(), v.clone()))
            .collect()
    }
    
    pub fn min_key<K: Clone, V>(map: &BTreeMap<K, V>) -> Option<K> {
        map.keys().next().cloned()
    }
    
    pub fn max_key<K: Clone, V>(map: &BTreeMap<K, V>) -> Option<K> {
        map.keys().next_back().cloned()
    }
    
    #[cfg(test)]
    mod tests {
        use super::*;
    
        #[test]
        fn sorted_iteration() {
            let m = sorted_map(vec![(3, 'c'), (1, 'a'), (2, 'b')]);
            let keys: Vec<_> = m.keys().cloned().collect();
            assert_eq!(keys, vec![1, 2, 3]);
        }
        #[test]
        fn range_works() {
            let m: BTreeMap<i32, i32> = (0..10).map(|i| (i, i * i)).collect();
            let r = range_query(&m, &3, &5);
            assert_eq!(r, vec![(3, 9), (4, 16), (5, 25)]);
        }
        #[test]
        fn min_max_keys() {
            let m = sorted_map(vec![(5, 'e'), (1, 'a'), (9, 'i')]);
            assert_eq!(min_key(&m), Some(1));
            assert_eq!(max_key(&m), Some(9));
        }
    }
    ✓ Tests Rust test suite
    #[cfg(test)]
    mod tests {
        use super::*;
    
        #[test]
        fn sorted_iteration() {
            let m = sorted_map(vec![(3, 'c'), (1, 'a'), (2, 'b')]);
            let keys: Vec<_> = m.keys().cloned().collect();
            assert_eq!(keys, vec![1, 2, 3]);
        }
        #[test]
        fn range_works() {
            let m: BTreeMap<i32, i32> = (0..10).map(|i| (i, i * i)).collect();
            let r = range_query(&m, &3, &5);
            assert_eq!(r, vec![(3, 9), (4, 16), (5, 25)]);
        }
        #[test]
        fn min_max_keys() {
            let m = sorted_map(vec![(5, 'e'), (1, 'a'), (9, 'i')]);
            assert_eq!(min_key(&m), Some(1));
            assert_eq!(max_key(&m), Some(9));
        }
    }

    Deep Comparison

    OCaml vs Rust: Btreemap Ordered

    Overview

    See the example.rs and example.ml files for detailed implementations.

    Key Differences

    AspectOCamlRust
    Type systemHindley-MilnerOwnership + traits
    MemoryGCZero-cost abstractions
    MutabilityExplicit refmut keyword
    Error handlingOption/ResultResult<T, E>

    See README.md for detailed comparison.

    Exercises

  • Word frequency sorted: Count word frequencies from a string using BTreeMap<&str, usize>; print the top 5 words sorted alphabetically, then resort by frequency using a BinaryHeap.
  • Range sum: Given a BTreeMap<i32, i32> (key → value), implement range_sum(map, lo, hi) that returns the sum of values for all keys in [lo, hi] using .range().
  • Interval overlap: Use a BTreeMap<i32, i32> where keys are interval starts and values are interval ends; implement a query that finds all intervals overlapping a given point using .range(..=point).
  • Open Source Repos