如何简洁地填充2D HashMap?

时间:2017-03-23 18:02:33

标签: hashmap rust

是否有一种简单的方法来填充二维HashMap?在C ++中,我可以做以下几点:

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

在我的Rust项目中,我正在尝试填写类似的地图:

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

我能想到的唯一方法是构建每个子地图,然后将它们移动到超级地图中,如下所示:

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);

有更简洁的方法吗?

上面的代码是我的实际用例的简化,它使用枚举作为HashMap的索引:

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

所有变体都没有值。我正在使用此语法进行HashMap

的查找
let value = map[Example::Variant1][Example::Variant2];

3 个答案:

答案 0 :(得分:9)

在Rust中,元组工作得非常好。您可能应该使用library(data.table) my.max <- function(x){ if(all(is.na(x))){ return("9999-12-01") }else{ return(max(x, na.rm = T)) } } DT <- data.table("Date1" = c(as.Date("2015-12-30"),NA, NA), "Date2" = c(as.Date("2013-02-04"), as.Date("2014-01-01"), NA)) print(DT) DT[ , "Max_Date" ] <- apply(DT, 1, my.max) print(DT) > DT <- data.table("Date1" = c(as.Date("2015-12-30"),NA, NA), "Date2" = c(as.Date("2013-02-04"), as.Date("2014-01-01"), NA)) > print(DT) Date1 Date2 1: 2015-12-30 2013-02-04 2: <NA> 2014-01-01 3: <NA> <NA> > DT[ , "Max_Date" ] <- apply(DT, 1, my.max) > print(DT) Date1 Date2 Max_Date 1: 2015-12-30 2013-02-04 2015-12-30 2: <NA> 2014-01-01 2014-01-01 3: <NA> <NA> 9999-12-01 。然后,你最终会得到一些非常接近C ++代码的东西。

HashMap<(i32, i32), i32>

当然,如果我们有一个像let mut map: HashMap<(i32, i32), i32> = HashMap::new(); sub1.insert((0, 0), 0); sub1.insert((0, 1), 1); // ... 这样的宏,那么会很好,an RFC is in the works。使用this answer中的宏,您可以写:

vec!

如果您使用枚举,则不会发生任何变化 - 只需确保为其获取let map = hashmap![(0,0) => 0, (0,1) => 1, ...]; EqPartialEq

Hash

答案 1 :(得分:7)

使用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]);
}

与C ++不同,构建嵌套的HashMap并不是隐含的 - 您必须非常明确地希望允许创建新的HashMap

the other answer不同,它保留了原始数据结构,并且只根据初始密钥获取整个地图的子集:

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

如果总是提供两个数字索引,那么元组是一个更好的解决方案,因为它更准确地模拟问题 - 只有一个真正的密钥,它恰好有两个组件。它也可能有更好的数据局部性,因为有一大块内存,而不是额外的指针追逐哈希散列所需要的。

答案 2 :(得分:2)

C ++代码片段的简洁归功于方便的operator [],如果地图中缺少值,它会自动默认构造值。虽然Rust默认情况下不这样做,但很容易告诉它这样做,如Shepmaster的回答所示。

为了避免拼出entry(key).or_insert_with(ValueType::new),可以将等效于C ++ operator []的方法添加到Rust的HashMap。毕竟,Rust有必要的工具 - 它支持使用traits向现有类型添加方法,并且它的特性大致相当于C ++的默认构造函数。

C ++表达式:

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

将写为以下Rust,使用返回引用的方法代替operator []

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

ensure将在一个特征中声明,该特征必须由想要获取该方法的代码导入:

use std::collections::HashMap;
use std::hash::Hash;

trait MapDefault<K, V: Default> {
    fn ensure(&mut self, key: K) -> &mut V;
}

...并且定义如下:

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)
    }
}

能够为IndexMut定义HashMap会很好,这会将表达式缩短为*map[0][0] = 0,这几乎就像C ++原版一样,但不幸的是Rust没有'允许实现其他模块类型的运算符。