rust 从给定的散列Map中提取条目并将它们传输到另一个散列Map

iovurdzv  于 2023-08-05  发布在  其他
关注(0)|答案(2)|浏览(151)

如何从HashMap中提取条目并将其传输到Rust中的另一个HashMap。在C++中,我可以通过以下方式

  1. #include <iostream>
  2. #include <unordered_map>
  3. void print(const std::unordered_map<std::string, int>& data) {
  4. for (const auto&[key, val] : data) {
  5. std::cout << key << ": " << val << "\n";
  6. }
  7. std::cout << std::endl;
  8. }
  9. std::unordered_map<std::string, int> extract(std::unordered_map<std::string, int>& mapp) {
  10. std::unordered_map<std::string, int> mappp;
  11. for (auto itr = mapp.begin(); itr != mapp.end(); ) {
  12. if (itr->second > 200) {
  13. auto prev_itr = itr++;
  14. mappp.insert(mapp.extract(prev_itr));
  15. } else {
  16. ++itr;
  17. }
  18. }
  19. return mappp;
  20. }
  21. int main() {
  22. std::unordered_map<std::string, int> mapp {
  23. {"abc", 123},
  24. {"def", 67},
  25. {"ghi", 380},
  26. {"jkl", 4376}
  27. };
  28. print(mapp);
  29. auto mappp = extract(mapp);
  30. print(mapp);
  31. print(mappp);
  32. }

字符串
Rust等价(extract函数不完整)

  1. use std::collections::HashMap;
  2. fn print(mapp: &HashMap<String, i32>) {
  3. for (key, val) in mapp {
  4. println!("{}: {}", key, val);
  5. }
  6. println!("");
  7. }
  8. fn extract(mapp: &mut HashMap<String, i32>) -> HashMap<String, i32> {
  9. // mapp.iter().filter(&|(_, val)| val > 200)
  10. HashMap::new()
  11. }
  12. fn main() {
  13. let mut mapp: HashMap<String, i32> = HashMap::new();
  14. mapp.insert("abc".to_string(), 123);
  15. mapp.insert("def".to_string(), 67);
  16. mapp.insert("ghi".to_string(), 380);
  17. mapp.insert("jkl".to_string(), 4376);
  18. print(&mapp);
  19. let mappp = extract(&mut mapp);
  20. print(&mapp);
  21. print(&mappp);
  22. }

b5buobof

b5buobof1#

由于drain_filter还不稳定,一个不太理想的选择(因为更多的分配和更多的流量)是将drain原始Map将其内容分发到 * 两个 * 目标,然后将旧Map设置为其中一个目标

  1. fn extract(mapp: &mut HashMap<String, i32>) -> HashMap<String, i32> {
  2. let mut old = HashMap::new();
  3. let mut new = HashMap::new();
  4. for (k, v) in mapp.drain() {
  5. if v > 200 {
  6. new.insert(k, v);
  7. } else {
  8. old.insert(k, v);
  9. }
  10. }
  11. *mapp = old;
  12. new
  13. }

字符串
一个可能稍微更有效的替代方案是将旧的条目收集到Vec中,然后extend()旧的Map:

  1. fn extract(mapp: &mut HashMap<String, i32>) -> HashMap<String, i32> {
  2. let mut old = Vec::new();
  3. let mut new = HashMap::new();
  4. for (k, v) in mapp.drain() {
  5. if v > 200 {
  6. new.insert(k, v);
  7. } else {
  8. old.push((k, v));
  9. }
  10. }
  11. mapp.extend(old);
  12. new
  13. }


它仍然需要重新散列和重新分发重新插入的条目,但对于较大的数据大小,它应该具有较低的内存高水位线,因为vec需要的空间比map少。它还可以避免丢弃与mapp相关联的分配,这取决于它的usd方式,此后可能会有用。
然而,一个更老的替代方案是直接依赖于hashbrown,这是标准Map的实际底层实现,以及it added drain_filter back in late 2019

展开查看全部
bjp0bcyl

bjp0bcyl2#

为了高效地完成这项工作,您需要extract_if*,目前它是每晚一次的。

  1. #![feature(hash_extract_if)]
  2. use std::hash::Hash;
  3. fn extract<K, V, F>(mapp: &mut HashMap<K, V>, condition: F) -> HashMap<K, V>
  4. where
  5. F: FnMut(&K, &mut V) -> bool,
  6. K: Eq + Hash,
  7. {
  8. mapp.extract_if(condition).collect()
  9. }

字符串
我把它设为通用的,这样就可以和其他方法进行比较。
如果你想在stable rust上使用它,你可以使用hashbrown crate,这是标准库目前在内部使用的。几乎所有来自std的API都是hashbrown格式的,还有一些东西,比如extract_if
在稳定的rust中,一个几乎完美的方法是使用retain。这需要克隆键并替换值,对于大多数哈希Map来说,这会很快,但是使用String键使这并不理想。

  1. fn extract<K, V, F>(mapp: &mut HashMap<K, V>, mut condition: F) -> HashMap<K, V>
  2. where
  3. F: FnMut(&K, &mut V) -> bool,
  4. K: Eq + Hash + Clone,
  5. V: Default,
  6. {
  7. let mut new_map = HashMap::new();
  8. mapp.retain(|k, v| {
  9. if condition(k, v) {
  10. let k = k.clone();
  11. let v = std::mem::take(v);
  12. new_map.insert(k, v);
  13. false
  14. } else {
  15. true
  16. }
  17. });
  18. new_map
  19. }


如果这些值没有实现Default,则可以使用占位符值。
其他的答案对于真正的通用键和值是好的。
下面是一个使用partition的例子。

  1. fn extract<K, V, F>(mapp: &mut HashMap<K, V>, mut condition: F) -> HashMap<K, V>
  2. where
  3. F: FnMut(&K, &V) -> bool,
  4. K: Eq + Hash,
  5. {
  6. let (new, old) = std::mem::take(mapp)
  7. .into_iter()
  8. .partition(|(k, v)| condition(k, v));
  9. *mapp = old;
  10. new
  11. }


这些方法的缺点是它会丢弃原始分配并创建两个新的分配,而其他方法会重用原始分配。

  • extract_if最近被重命名,所以目前它只能在nightly std库中在线可见。现在,stable std(显示从最后一个stable发布时起的每晚特性)称之为drain_filter
展开查看全部

相关问题