やりたいこと
Rustで配列内のデータをキー毎に集約する操作(group by)をしたい
#[derive(Clone, Debug)]
struct Hoge {
a: String,
b: i32,
}
let data = vec![
Hoge {
a: "a".to_string(),
b: 1,
},
Hoge {
a: "a".to_string(),
b: 1,
},
Hoge {
a: "b".to_string(),
b: 0,
},
Hoge {
a: "c".to_string(),
b: 2,
},
Hoge {
a: "a".to_string(),
b: 2,
},
];
といった時、dataをHoge.bで集約したいとする
最初に結論
foldでやるか、itertools.into_group_map_by がいいんじゃないかなと思う。
標準的アプロ―チ
foldを使う
// 型はBTreeMap<i32, Vec<Hoge>になる
let groups = data.iter().fold(
BTreeMap::new(),
|mut acc: BTreeMap<i32, Vec<Hoge>>, value| {
acc.entry(value.b)
.or_insert_with(Vec::new)
.push(value.to_owned());
acc
},
);
悪くはないのだが、個人的には直感的でないと感じてしまった。
grouping_by クレートを使う
こちらのクレート を使うと以下のように書ける
// 型はHashMap<i32, Vec<Hoge>になる
let groups = data.into_iter().grouping_by(|x| x.b.clone());
ただこのクレートはあまりメジャーでない
itertoolsを使う
itertools には、group_by
というメソッドがある。こちらはC#やSQLのGroupByとはちょっと違う動きをする。
ただ、バージョン10.0.0から into_group_map_by
というメソッドが追加されており、これだとC#やSQLのGroupByと同じ動きをするようだ。
// 型はHashMap<i32, Vec<Hoge>になる
let groups = data.into_iter().into_group_map_by(|x| x.b.clone());
このクレートなら他の便利なメソッドも使えるようになるし、これがよさそう。
まとめ
foldのやり方がピンとこなかったので、いろいろ調べました。itertoolsのgroup_by
は罠ですね。