如何将两个HashSet相交,同时将常用值移动到新的HashSet中?

时间:2019-05-03 18:18:53

标签: rust

Product.aggregate([
  {
    $match: {
      _id: "5cc9441feed4c258881c99cd"
    }
  },
  {
    $lookup: {
      from: "comments",
      localField: "_id",
      foreignField: "product",
      as: "comments"
    }
  },
  {
    $unwind: "$comments"
  },
  {
    $lookup: {
      from: "users",
      localField: "comments.user",
      foreignField: "_id",
      as: "comments.user"
    }
  },
  {
    $unwind: "$comments.user"
  },
  {
    $group: {
      _id: "$_id",
      // add other fields you want to include
      comments: {
        $addToSet: "$comments"
      }
    }
  },

])

我不再需要非相交的值。 如何从集合use std::collections::HashSet; let mut a: HashSet<T> = HashSet::new(); let mut b: HashSet<T> = HashSet::new(); let mut c: HashSet<T> = a.intersection(&b).collect(); // Error: a collection of type `std::collections::HashSet<T>` cannot be built from an iterator over elements of type `&T` a中窃取/移动数据到b中而不进行复制或克隆?理想情况下,这将具有理论上最佳的时间复杂度:O(min(a,b))。

3 个答案:

答案 0 :(得分:4)

由编译器强加的别名规则要求您前后移动值。尽管可以无条件地将值从集合中消耗掉。但是,如果我们跟踪应移动的值和应保留在新集中的值,则可以将某些值发回。然后,retain允许我们从第二组中删除公用值。

use std::collections::HashSet;
use std::hash::Hash;

/// Extracts the common values in `a` and `b` into a new set.
fn inplace_intersection<T>(a: &mut HashSet<T>, b: &mut HashSet<T>) -> HashSet<T>
where
    T: Hash,
    T: Eq,
{
    let x: HashSet<(T, bool)> = a
        .drain()
        .map(|v| {
            let intersects = b.contains(&v);
            (v, intersects)
        })
        .collect();

    let mut c = HashSet::new();
    for (v, is_inter) in x {
        if is_inter {
            c.insert(v);
        } else {
            a.insert(v);
        }
    }

    b.retain(|v| !c.contains(&v));

    c
}

使用:

use itertools::Itertools;  // for .sorted()

let mut a: HashSet<_> = [1, 2, 3].iter().cloned().collect();
let mut b: HashSet<_> = [4, 2, 3].iter().cloned().collect();

let c = inplace_intersection(&mut a, &mut b);

let a: Vec<_> = a.into_iter().sorted().collect();
let b: Vec<_> = b.into_iter().sorted().collect();
let c: Vec<_> = c.into_iter().sorted().collect();
assert_eq!(&a, &[1]);
assert_eq!(&b, &[4]);
assert_eq!(&c, &[2, 3]);

Playground

答案 1 :(得分:3)

或者,如果您可以对这些集合本身拥有所有权,并且不关心在其他集合中保留非相交的值,则可以执行以下操作:

use std::hash::Hash;
use std::collections::HashSet;

fn intersection<T: Eq + Hash>(a: HashSet<T>, b: &HashSet<T>) -> HashSet<T> {
    a.into_iter().filter(|e| b.contains(e)).collect()
}

这将b中包含的a中的元素收集到一个新的HashSet中。

答案 2 :(得分:2)

另一种解决方案,类似于E_net4的解决方案,但是该解决方案不涉及先清空然后重新填充第一组。恕我直言,它也更容易阅读。

fn inplace_intersection<T>(a: &mut HashSet<T>, b: &mut HashSet<T>) -> HashSet<T>
where
    T: Hash,
    T: Eq,
{
    let mut c = HashSet::new();

    for v in a.iter() {
        if let Some(found) = b.take(v) {
            c.insert(found);
        }
    }

    a.retain(|v| !c.contains(&v));

    c
}

Playground Link

写完这个之后,我意识到它可以变得更简单:

fn inplace_intersection<T>(a: &mut HashSet<T>, b: &mut HashSet<T>) -> HashSet<T>
where
    T: Hash,
    T: Eq,
{
    let c: HashSet<T> = a.iter().filter_map(|v| b.take(v)).collect();

    a.retain(|v| !c.contains(&v));

    c
}

Playground Link