How do I get 2D HashMap compression?

Is there an easy way to populate a 2-dimensional HashMap ? In C ++, I could do something like:

 std::unordered_map<int,std::unordered_map<int,int>> 2dmap; //... 2dmap[0][0] = 0; 2dmap[0][1] = 1; //... 

In my Rust project, I am trying to populate a similar map:

 let mut map: HashMap<i32, HashMap<i32, i32>> = HashMap::new(); //...fill the hash map here. 

The only way I can do this is to build each subframe map and then move them to a super map, something like this:

 let mut sub1: HashMap<i32, i32> = HashMap::new(); sub1.insert(0, 0); sub1.insert(1, 1); let mut map: HashMap<i32, HashMap<i32, i32>>::new(); map.insert(0, sub1); 

Is there a shorter way to do this?

The above code simplifies my actual use case, which uses enumeration as a HashMap index:

 enum Example { Variant1, Variant2, //... } 

None of the options matter. I use this syntax to search from my HashMap :

 let value = map[Example::Variant1][Example::Variant2]; 
+7
hashmap rust
source share
3 answers

In Rust, tuples work very well. You should probably just use HashMap<(i32, i32), i32> . Then you get something very close to C ++ code.

 let mut map: HashMap<(i32, i32), i32> = HashMap::new(); sub1.insert((0, 0), 0); sub1.insert((0, 1), 1); // ... 

Naturally, it would be nice if we had a macro like vec! , and the RFC is in operation . Using the macro from this answer , you can write:

 let map = hashmap![(0,0) => 0, (0,1) => 1, ...]; 

Nothing changes if you use an enumeration - just make sure you get Eq , PartialEq and Hash for it.

 let mut sub1: HashMap<(Example, Example), i32> = HashMap::new(); sub1.insert((Example::Variant1, Example::Variant1), 0); sub1.insert((Example::Variant1, Example::Variant2), 1); // ... 
+9
source share

Use the entry API:

 use std::collections::HashMap; fn main() { let mut map = HashMap::new(); map.entry(0).or_insert_with(HashMap::new).insert(0, 0); map.entry(0).or_insert_with(HashMap::new).insert(1, 1); println!("{:?}", map); println!("{}", map[&0][&1]); } 

Unlike C ++, building a nested HashMap not implicit - it should be very clear to you that you want to allow the creation of a new HashMap .


Unlike the other answer , this preserves the original data structure and the ability to get a subset of the common map based on only the initial key:

 println!("{:?}", map.get(&0)); println!("{:?}", map.get(&0).and_then(|m| m.get(&1))); 

If you always provide numerical indices, then a tuple is a much better solution, because it models the problem more precisely - there is only one true key, it just has two components. It will also probably have better data localization, since there will be one large memory slab, as opposed to an extra level of pointer chasing hash hashes.

+6
source share

The C ++ code fragment owes its brevity to the convenient operator [] , which automatically creates a default value if it is not on the map. While Rust does not do this by default, it’s simple enough to say that this is done, as shown in Shepmaster's answer.

To avoid using entry(key).or_insert_with(ValueType::new) , you can add a method equivalent to C ++ operator [] in Rust HashMap . In the end, Rust has the necessary tools β€” it supports adding methods to existing types using attributes and has a trait roughly equivalent to the default C ++ constructor.

C ++ expressions:

 map[0][0] = 0; map[0][1] = 1; 

will be written as the following Rust, using a method that returns a link instead of operator [] :

 *map.ensure(0).ensure(0) = 0; *map.ensure(0).ensure(1) = 1; 

ensure will be declared in the attribute that must be imported by the code that the method wants to receive:

 use std::collections::HashMap; use std::hash::Hash; trait MapDefault<K, V: Default> { fn ensure(&mut self, key: K) -> &mut V; } 

... and is defined as follows:

 impl<K: Eq + Hash, V: Default> MapDefault<K, V> for HashMap<K, V> { fn ensure(&mut self, key: K) -> &mut V { self.entry(key).or_insert_with(V::default) } } 

It would be nice to define IndexMut for HashMap , which would reduce the expression to *map[0][0] = 0 , which is almost similar to the original C ++, but, unfortunately, Rust does not allow implementing operators for types from other modules.

+2
source share

All Articles