Loading page…
Rust walkthroughs
Loading page…
IndexMap is a crate that provides a hash map with insertion order preservation. Unlike std::collections::HashMap which has arbitrary iteration order, IndexMap maintains the order in which keys were inserted. It also provides O(1) element access by index.
Key concepts:
When to use IndexMap:
When NOT to use IndexMap:
use indexmap::IndexMap;
fn main() {
let mut map = IndexMap::new();
map.insert("apple", 3);
map.insert("banana", 2);
map.insert("cherry", 5);
// Iteration preserves insertion order
for (key, value) in &map {
println!("{}: {}", key, value);
}
}use indexmap::IndexMap;
fn main() {
let mut map = IndexMap::new();
map.insert("first", 1);
map.insert("second", 2);
map.insert("third", 3);
// Access by index
let (key, value) = map.get_index(0).unwrap();
println!("Index 0: {} = {}", key, value);
let (key, value) = map.get_index(1).unwrap();
println!("Index 1: {} = {}", key, value);
// Get index of a key
let idx = map.get_index_of("second");
println!("'second' is at index {:?}", idx);
}use indexmap::IndexSet;
fn main() {
let mut set = IndexSet::new();
set.insert("red");
set.insert("green");
set.insert("blue");
// Iterate in insertion order
for color in &set {
println!("Color: {}", color);
}
// Get by index
let color = set.get_index(0).unwrap();
println!("First color: {}", color);
}use indexmap::IndexMap;
fn main() {
let mut map = IndexMap::new();
map.insert("a", 10);
map.insert("b", 20);
map.insert("c", 30);
// Mutate value by index
if let Some((key, value)) = map.get_index_mut(1) {
println!("Changing {}", key);
*value = 25;
}
println!("Updated map: {:?}", map);
}use indexmap::IndexMap;
fn main() {
let mut map = IndexMap::new();
map.insert("a", 1);
map.insert("b", 2);
map.insert("c", 3);
map.insert("d", 4);
// Remove by key (shifts subsequent elements)
let removed = map.remove("b");
println!("Removed: {:?}", removed);
// Now "c" is at index 1
for (i, (k, v)) in map.iter().enumerate() {
println!("Index {}: {} = {}", i, k, v);
}
}use indexmap::IndexMap;
fn main() {
let mut map = IndexMap::new();
map.insert("a", 1);
map.insert("b", 2);
map.insert("c", 3);
// Swap indices (fast - doesn't shift)
map.swap_indices(0, 2);
for (k, v) in &map {
println!("{}: {}", k, v);
}
// Swap remove by key (O(1) but changes order)
map.swap_remove("b");
println!("After swap_remove: {:?}", map);
}use indexmap::IndexMap;
fn main() {
let mut map = IndexMap::new();
// Use entry API similar to HashMap
map.entry("apple").or_insert(5);
map.entry("banana").or_insert(3);
map.entry("apple").and_modify(|v| *v += 1);
// Entry with default computation
map.entry("cherry").or_insert_with(|| {
println!("Creating default for cherry");
10
});
println!("Map: {:?}", map);
}use indexmap::IndexMap;
use serde_json::json;
fn main() {
let mut map = IndexMap::new();
map.insert("name", "Alice");
map.insert("age", "30");
map.insert("city", "Boston");
// Keys will be serialized in insertion order
let json = serde_json::to_string_pretty(&map).unwrap();
println!("{}", json);
}use indexmap::IndexMap;
fn main() {
let mut map = IndexMap::new();
map.insert("a", 1);
map.insert("c", 3);
// Insert at specific position
map.insert_before("c", "b", 2);
// Keys are now in order: a, b, c
for (k, v) in &map {
println!("{}: {}", k, v);
}
}use indexmap::IndexMap;
fn main() {
let mut map = IndexMap::new();
map.insert("a", 1);
map.insert("b", 2);
map.insert("c", 3);
// Iterate over keys
for key in map.keys() {
println!("Key: {}", key);
}
// Iterate over values
for value in map.values() {
println!("Value: {}", value);
}
// Iterate with indices
for (idx, (key, value)) in map.iter().enumerate() {
println!("Index {}: {} = {}", idx, key, value);
}
}use indexmap::IndexMap;
fn main() {
let mut map = IndexMap::new();
map.insert("a", 1);
map.insert("b", 2);
map.insert("c", 3);
// Re-inserting existing key keeps its position
map.insert("b", 20);
// But you can move it
map.move_index(1, 2); // Move "b" from index 1 to index 2
for (k, v) in &map {
println!("{}: {}", k, v);
}
}use indexmap::IndexMap;
fn main() {
// Pre-allocate capacity
let mut map: IndexMap<&str, i32> = IndexMap::with_capacity(100);
println!("Capacity: {}", map.capacity());
for i in 0..50 {
map.insert(&format!("key{}", i), i);
}
println!("Len: {}, Capacity: {}", map.len(), map.capacity());
// Shrink to fit
map.shrink_to_fit();
println!("After shrink: Capacity: {}", map.capacity());
}use indexmap::IndexMap;
fn main() {
let mut map = IndexMap::new();
map.insert("a", 1);
map.insert("b", 2);
map.insert("c", 3);
map.insert("d", 4);
// Retain only even values
map.retain(|_k, v| *v % 2 == 0);
println!("After retain: {:?}", map);
}use indexmap::IndexMap;
use std::collections::HashMap;
fn main() {
// From Vec of tuples
let map: IndexMap<&str, i32> = vec![
("a", 1),
("b", 2),
("c", 3),
].into_iter().collect();
println!("From Vec: {:?}", map);
// To HashMap (loses order)
let hash_map: HashMap<_, _> = map.into_iter().collect();
println!("To HashMap: {:?}", hash_map);
}use indexmap::IndexMap;
fn main() {
let mut map = IndexMap::new();
map.insert("apple", 3);
map.insert("banana", 2);
map.insert("cherry", 5);
// Sort by value
map.sort_by(|_k1, v1, _k2, v2| v1.cmp(v2));
for (k, v) in &map {
println!("{}: {}", k, v);
}
}use indexmap::IndexMap;
fn main() {
let mut map = IndexMap::new();
map.insert("cherry", 5);
map.insert("apple", 3);
map.insert("banana", 2);
// Sort by key
map.sort_keys();
for (k, v) in &map {
println!("{}: {}", k, v);
}
}use indexmap::IndexMap;
fn main() {
let mut map = IndexMap::new();
map.insert("a", 1);
map.insert("b", 2);
map.insert("c", 3);
// Get first entry
let first = map.first();
println!("First: {:?}", first);
// Get last entry
let last = map.last();
println!("Last: {:?}", last);
// Pop first
let popped = map.shift_remove_index(0);
println!("Popped first: {:?}", popped);
}use indexmap::IndexMap;
fn main() {
let mut map = IndexMap::new();
map.insert("a", 1);
map.insert("b", 2);
map.insert("c", 3);
map.insert("d", 4);
map.insert("e", 5);
// Split at index
let map2 = map.split_off(3);
println!("Original: {:?}", map);
println!("Split off: {:?}", map2);
}use indexmap::IndexMap;
fn main() {
let mut map1 = IndexMap::new();
map1.insert("a", 1);
map1.insert("b", 2);
let mut map2 = IndexMap::new();
map2.insert("c", 3);
map2.insert("a", 10); // Duplicate key
// Extend (keeps order, overwrites duplicates)
map1.extend(map2);
println!("Extended: {:?}", map1);
}use indexmap::IndexMap;
fn main() {
let mut map = IndexMap::new();
map.insert("a", 1);
map.insert("b", 2);
map.insert("c", 3);
map.insert("d", 4);
// Partition into two maps
let (even, odd): (IndexMap<_, _>, IndexMap<_, _>) =
map.into_iter().partition(|(_, v)| *v % 2 == 0);
println!("Even: {:?}", even);
println!("Odd: {:?}", odd);
}use indexmap::IndexMap;
struct Config {
values: IndexMap<String, String>,
}
impl Config {
fn new() -> Self {
Self {
values: IndexMap::new(),
}
}
fn set(&mut self, key: &str, value: &str) {
self.values.insert(key.to_string(), value.to_string());
}
fn get(&self, key: &str) -> Option<&String> {
self.values.get(key)
}
fn keys_in_order(&self) -> Vec<&String> {
self.values.keys().collect()
}
}
fn main() {
let mut config = Config::new();
config.set("host", "localhost");
config.set("port", "8080");
config.set("debug", "true");
println!("Keys in order: {:?}", config.keys_in_order());
}use indexmap::IndexMap;
struct Cache {
data: IndexMap<String, String>,
capacity: usize,
}
impl Cache {
fn new(capacity: usize) -> Self {
Self {
data: IndexMap::new(),
capacity,
}
}
fn get(&mut self, key: &str) -> Option<&String> {
if self.data.contains_key(key) {
// Move to end (most recently used)
let idx = self.data.get_index_of(key).unwrap();
self.data.move_index(idx, self.data.len() - 1);
}
self.data.get(key)
}
fn put(&mut self, key: String, value: String) {
if self.data.len() >= self.capacity {
// Remove first (least recently used)
self.data.shift_remove_index(0);
}
self.data.insert(key, value);
}
}
fn main() {
let mut cache = Cache::new(3);
cache.put("a".to_string(), "1".to_string());
cache.put("b".to_string(), "2".to_string());
cache.put("c".to_string(), "3".to_string());
cache.get("a"); // Access "a" - moves to end
cache.put("d".to_string(), "4".to_string()); // Evicts "b"
println!("Cache contents: {:?}", cache.data);
}IndexMap Type Signature:
use indexmap::IndexMap;
let map: IndexMap<K, V> = IndexMap::new();Key Methods:
| Method | Description |
|--------|-------------|
| insert(k, v) | Insert entry |
| get(k) | Get value by key |
| get_index(i) | Get (key, value) by index |
| get_index_of(k) | Get index of key |
| remove(k) | Remove by key (shifts) |
| swap_remove(k) | Remove by key (swaps, O(1)) |
| shift_remove_index(i) | Remove by index |
| swap_indices(i, j) | Swap two entries |
| move_index(from, to) | Move entry to new position |
| sort_keys() | Sort by key |
| sort_by(cmp) | Sort by comparator |
| first() | Get first entry |
| last() | Get last entry |
| split_off(at) | Split into two maps |
IndexMap vs HashMap vs BTreeMap:
| Feature | IndexMap | HashMap | BTreeMap | |---------|----------|---------|----------| | Iteration order | Insertion | Arbitrary | Sorted | | Index access | O(1) | No | No | | Key lookup | O(1) | O(1) | O(log n) | | Ordered keys | Yes (insertion) | No | Yes (sorted) | | Memory overhead | Higher | Lower | Lower |
Key Points:
get_index() for position-based accessget_index_of() to find key's positionremove() shifts, swap_remove() swaps (faster but changes order)sort_keys() or sort_by() for sorted orderIndexSet for ordered sets