< 返回版块

ltoddy 发表于 2020-03-03 16:27

我有一个需求, 在一个Vec里面, 我要对立面的元素进行聚合变成一个HashMap, 但是Rust的Iterator没有提供类似Java stream的groupingBy或者collect(toMap)这样的功能.

我的实现是(粗糙): 就是顺序读取Vec里面的元素,然后判断这个元素有没有在HashMap里面(元素是个结构体, 通过里面的某个成员去判断), 没有则insert进去, 如果有, 则从HashMap中get出已经存储的数据, 然后这两者进行计算.然后再次insert进去.

源代码在: https://github.com/ltoddy/cloc-rs/blob/master/src/detail.rs 的最下面的那个函数的if-else里面.

每次cargo check的时候, 都会报warn, 所以想寻找一个更好的写法.

评论区

写评论
lagudomeze 2020-03-04 06:37

FromIterator 在 hash map里有实现。

https://doc.rust-lang.org/std/iter/trait.FromIterator.html

https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.collect

大致可以这么做 v.iter().map(T转换为(K,V)).collect()

AlephAlpha 2020-03-03 17:36
if let Some(d) = self.inner.get(&language) {
    self.inner.insert(language, *d + detail);
} else {
    self.inner.insert(language, detail);
}

这里可以改成

self.inner.entry(language).and_modify(|e| *e += detail).or_insert(detail);
liangyongrui 2020-03-03 17:09

我看了一下,你的代码适合用 or_insert_with

对以下内容的回复:

作者 ltoddy 2020-03-03 17:05

感谢.

对以下内容的回复:

liangyongrui 2020-03-03 16:53
use std::collections::HashMap;
fn main() {
    let t = vec![(1, 2), (3, 4), (1, 6)];
    let mut t2 = HashMap::new();
    t2.insert(1, 8);
    t2.insert(3, 4);
    let res = t.into_iter().fold(HashMap::new(), |mut acc, x| {
        *acc.entry(x.0).or_insert(0) += x.1;
        acc
    });
    assert_eq!(res, t2);
}

对以下内容的回复:

作者 ltoddy 2020-03-03 16:46

例如:

[(key1, 1), (key2, 2), (key1, 3)]

=> 

{
  key1 => 4,
  key2 => 2,
}
liangyongrui 2020-03-03 16:41

这样?

use std::collections::HashMap;
fn main() {
    let t = vec![(1, 2), (3, 4), (5, 6)];
    let mut t2 = HashMap::new();
    t2.insert(1, 2);
    t2.insert(3, 4);
    t2.insert(5, 6);

    let res = t.into_iter().fold(HashMap::new(), |mut acc, x| {
        acc.insert(x.0, x.1);
        acc
    });
    assert_eq!(res, t2);
}
1 共 7 条评论, 1 页