Ordered Collections

split_off()

The .split_off(&key) method on BTreeMap<K, V> and BTreeSet<T> partitions an ordered collection at a split key into two independent collections: self retains all keys strictly less than key (< key), and the returned collection contains all keys greater than or equal to key (>= key).

Signatures

// On BTreeMap<K, V>:
pub fn split_off<Q>(&mut self, key: &Q) -> BTreeMap<K, V>
where
    K: Borrow<Q> + Ord,
    Q: Ord + ?Sized;

// On BTreeSet<T>:
pub fn split_off<Q>(&mut self, value: &Q) -> BTreeSet<T>
where
    T: Borrow<Q> + Ord,
    Q: Ord + ?Sized;
Before: map = { 10: "a", 20: "b", 30: "c", 40: "d" }

let right = map.split_off(&25); (or map.split_off(&30))
  ├── map   (<  split_key): { 10: "a", 20: "b" }
  └── right (>= split_key): { 30: "c", 40: "d" }

Notice that key does not need to be present in the map or set—split_off(&q) splits along the ordering boundary < q vs. >= q for any value q: &Q.

Partitioning a BTreeMap and BTreeSet by Key Threshold

use std::collections::{BTreeMap, BTreeSet};

fn main() {
    // 1. Splitting a BTreeMap at a threshold timestamp:
    let mut archive = BTreeMap::from([
        (100_u64, "old_1"),
        (200, "old_2"),
        (300, "recent_1"),
        (400, "recent_2"),
    ]);

    // `archive` keeps `< 300`; `active` receives `>= 300`:
    let active = archive.split_off(&300);
    println!("Archive (< 300): {archive:?}");
    println!("Active (>= 300): {active:?}");

    // 2. Splitting a BTreeSet using a threshold value that is not in the set:
    let mut low_scores = BTreeSet::from([15, 40, 65, 85, 95]);
    let passing_scores = low_scores.split_off(&60);

    println!("Failing (< 60):  {low_scores:?}");
    println!("Passing (>= 60): {passing_scores:?}");
}
Archive (< 300): {100: "old_1", 200: "old_2"}
Active (>= 300): {300: "recent_1", 400: "recent_2"}
Failing (< 60):  {15, 40}
Passing (>= 60): {65, 85, 95}

Difference Between Vec::split_off(index) and BTreeMap::split_off(&key)

Do not confuse the parameter meaning on Vec vs. BTreeMap / BTreeSet:

  • Vec::split_off(at: usize) splits by positional index at (0..at vs. at..len).
  • BTreeMap::split_off(key: &Q) and BTreeSet::split_off(key: &Q) split by key value comparison (k < *key vs. k >= *key).