Skip to content

Commit c631b75

Browse files
authored
Merge pull request #1 from dfaust/master
Add multimap! macro, Debug and PartialEq.
2 parents d3edd93 + b62ff85 commit c631b75

File tree

2 files changed

+118
-10
lines changed

2 files changed

+118
-10
lines changed

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
# Multi-Map
22

33
[![Build Status](https://travis-ci.org/thejpster/multi-map.svg?branch=master)](https://travis-ci.org/thejpster/multi-map)
4+
[![Crate version](https://img.shields.io/crates/v/multi-map.svg)](https://crates.io/crates/multi-map)
5+
[![Documentation](https://img.shields.io/badge/documentation-docs.rs-df3600.svg)](https://docs.rs/multi-map)
46

5-
Like a std::collection::HashMap, but allows you to use either of two different keys to retrieve items.
7+
Like a `std::collection::HashMap`, but allows you to use either of two different keys to retrieve items.
68

79
For more details, see [the Github.io page](http://thejpster.github.io/multi-map/).
8-

src/lib.rs

Lines changed: 115 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,38 @@
11
//! # multi-map
22
//!
3-
//! MultiMap is like a std::collection::HashMap, but allows you to use either of
3+
//! `MultiMap` is like a `std::collection::HashMap`, but allows you to use either of
44
//! two different keys to retrieve items.
55
//!
66
//! The keys have two distinct types - `K1` and `K2` - which may be the same.
77
//! Accessing on the primary `K1` key is via the usual `get`, `get_mut` and
88
//! `remove_alt` methods, while accessing via the secondary `K2` key is via new
99
//! `get_alt`, `get_mut_alt` and `remove_alt` methods. The value is of type `V`.
1010
//!
11-
//! Internally, two HashMaps are created - a main one on `<K1, (K2,
11+
//! Internally, two `HashMap`s are created - a main one on `<K1, (K2,
1212
//! V)>` and a second one on `<K2, K1>`. The `(K2, V)` tuple is so
1313
//! that when an item is removed using the `K1` key, the appropriate `K2`
1414
//! value is available so the `K2->K1` map can be removed from the second
15-
//! HashMap, to keep them in sync.
15+
//! `MultiMap`, to keep them in sync.
1616
//!
17-
//! Using two HashMaps instead of one naturally brings a slight performance
18-
//! and memory penalty. Notably, indexing by `K2` requires two HashMap lookups.
17+
//! Using two `HashMap`s instead of one naturally brings a slight performance
18+
//! and memory penalty. Notably, indexing by `K2` requires two `HashMap` lookups.
1919
//!
2020
//! ```
21-
//! # extern crate multi_map;
21+
//! extern crate multi_map;
2222
//! use multi_map::MultiMap;
23+
//!
2324
//! # fn main() {
2425
//! #[derive(Hash,Clone,PartialEq,Eq)]
2526
//! enum ThingIndex {
2627
//! IndexOne,
2728
//! IndexTwo,
2829
//! IndexThree,
2930
//! };
31+
//!
3032
//! let mut map = MultiMap::new();
3133
//! map.insert("One", ThingIndex::IndexOne, 1);
3234
//! map.insert("Two", ThingIndex::IndexTwo, 2);
35+
//!
3336
//! assert!(*map.get_alt(&ThingIndex::IndexOne).unwrap() == 1);
3437
//! assert!(*map.get(&"Two").unwrap() == 2);
3538
//! assert!(map.remove_alt(&ThingIndex::IndexTwo).unwrap() == 2);
@@ -40,7 +43,9 @@ use std::collections::HashMap;
4043
use std::collections::hash_map::Iter;
4144
use std::hash::Hash;
4245
use std::borrow::Borrow;
46+
use std::fmt::{self, Debug};
4347

48+
#[derive(Eq)]
4449
pub struct MultiMap<K1: Eq + Hash, K2: Eq + Hash, V> {
4550
value_map: HashMap<K1, (K2, V)>,
4651
key_map: HashMap<K2, K1>,
@@ -64,6 +69,16 @@ impl<K1: Eq + Hash + Clone, K2: Eq + Hash + Clone, V> MultiMap<K1, K2, V> {
6469
}
6570
}
6671

72+
/// Creates an empty MultiMap with the specified capacity.
73+
///
74+
/// The multi map will be able to hold at least `capacity` elements without reallocating. If `capacity` is 0, the multi map will not allocate.
75+
pub fn with_capacity(capacity: usize) -> MultiMap<K1, K2, V> {
76+
MultiMap {
77+
value_map: HashMap::with_capacity(capacity),
78+
key_map: HashMap::with_capacity(capacity),
79+
}
80+
}
81+
6782
/// Insert an item into the MultiMap. You must supply both keys to insert
6883
/// an item. The keys cannot be modified at a later date, so if you only
6984
/// have one key at this time, use a placeholder value for the second key
@@ -99,7 +114,7 @@ impl<K1: Eq + Hash + Clone, K2: Eq + Hash + Clone, V> MultiMap<K1, K2, V> {
99114
pub fn get_alt(&self, key: &K2) -> Option<&V> {
100115
let mut result = None;
101116
if let Some(key_a) = self.key_map.get(key) {
102-
if let Some(pair) = self.value_map.get(&key_a) {
117+
if let Some(pair) = self.value_map.get(key_a) {
103118
result = Some(&pair.1)
104119
}
105120
}
@@ -111,7 +126,7 @@ impl<K1: Eq + Hash + Clone, K2: Eq + Hash + Clone, V> MultiMap<K1, K2, V> {
111126
pub fn get_mut_alt(&mut self, key: &K2) -> Option<&mut V> {
112127
let mut result = None;
113128
if let Some(key_a) = self.key_map.get(key) {
114-
if let Some(pair) = self.value_map.get_mut(&key_a) {
129+
if let Some(pair) = self.value_map.get_mut(key_a) {
115130
result = Some(&mut pair.1)
116131
}
117132
}
@@ -157,6 +172,62 @@ impl<K1: Eq + Hash + Clone, K2: Eq + Hash + Clone, V> MultiMap<K1, K2, V> {
157172
}
158173
}
159174

175+
impl<K1: Eq + Hash, K2: Eq + Hash, V: Eq> PartialEq for MultiMap<K1, K2, V> {
176+
fn eq(&self, other: &MultiMap<K1, K2, V>) -> bool {
177+
self.value_map.eq(&other.value_map)
178+
}
179+
}
180+
181+
impl<K1: Eq + Hash + Debug, K2: Eq + Hash + Debug, V: Debug> fmt::Debug for MultiMap<K1, K2, V> {
182+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
183+
f.debug_map().entries(self.value_map.iter().map(|(key_one, &(ref key_two, ref value))| ((key_one, key_two), value))).finish()
184+
}
185+
}
186+
187+
#[macro_export]
188+
/// Create a `MultiMap` from a list of key-value tuples
189+
///
190+
/// ## Example
191+
///
192+
/// ```
193+
/// #[macro_use]
194+
/// extern crate multi_map;
195+
/// use multi_map::MultiMap;
196+
///
197+
/// # fn main() {
198+
/// #[derive(Hash,Clone,PartialEq,Eq)]
199+
/// enum ThingIndex {
200+
/// IndexOne,
201+
/// IndexTwo,
202+
/// IndexThree,
203+
/// };
204+
///
205+
/// let map = multimap!{
206+
/// "One", ThingIndex::IndexOne => 1,
207+
/// "Two", ThingIndex::IndexTwo => 2,
208+
/// };
209+
///
210+
/// assert!(*map.get_alt(&ThingIndex::IndexOne).unwrap() == 1);
211+
/// assert!(*map.get(&"Two").unwrap() == 2);
212+
/// # }
213+
/// ```
214+
macro_rules! multimap {
215+
(@single $($x:tt)*) => (());
216+
(@count $($rest:expr),*) => (<[()]>::len(&[$(multimap!(@single $rest)),*]));
217+
218+
($($key1:expr, $key2:expr => $value:expr,)+) => { multimap!($($key1, $key2 => $value),+) };
219+
($($key1:expr, $key2:expr => $value:expr),*) => {
220+
{
221+
let _cap = multimap!(@count $($key1),*);
222+
let mut _map = MultiMap::with_capacity(_cap);
223+
$(
224+
_map.insert($key1, $key2, $value);
225+
)*
226+
_map
227+
}
228+
};
229+
}
230+
160231
mod test {
161232

162233
#[test]
@@ -195,6 +266,42 @@ mod test {
195266
assert!(*map.get(&2).unwrap() == String::from("Zwei!"));
196267
assert!(map.get_alt(&"Three") == None);
197268
assert!(map.get(&3) == None);
269+
}
270+
271+
#[test]
272+
fn macro_test() {
273+
use ::MultiMap;
274+
275+
let map: MultiMap<i32, &str, String> = MultiMap::new();
276+
277+
assert_eq!(map, multimap!{});
278+
279+
let mut map = MultiMap::new();
280+
map.insert(1, "One", String::from("Eins"));
281+
282+
assert_eq!(map, multimap!{
283+
1, "One" => String::from("Eins"),
284+
});
285+
286+
assert_eq!(map, multimap!{
287+
1, "One" => String::from("Eins")
288+
});
289+
290+
let mut map = MultiMap::new();
291+
map.insert(1, "One", String::from("Eins"));
292+
map.insert(2, "Two", String::from("Zwei"));
293+
map.insert(3, "Three", String::from("Drei"));
294+
295+
assert_eq!(map, multimap!{
296+
1, "One" => String::from("Eins"),
297+
2, "Two" => String::from("Zwei"),
298+
3, "Three" => String::from("Drei"),
299+
});
198300

301+
assert_eq!(map, multimap!{
302+
1, "One" => String::from("Eins"),
303+
2, "Two" => String::from("Zwei"),
304+
3, "Three" => String::from("Drei")
305+
});
199306
}
200307
}

0 commit comments

Comments
 (0)