是否有一种简单的方法来填充二维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];
答案 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, ...];
,Eq
和PartialEq
。
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没有'允许实现其他模块类型的运算符。