使用HashMap

时间:2015-08-14 13:39:48

标签: iterator rust

我正在尝试使用类似SQL的连接迭代器来扩展bluss的rust-itertools。我使用散列连接策略遇到了RIGHT OUTER JOIN的特殊问题(策略本身实际上非常简单)。

迭代器适配器struct需要2个输入迭代器,其中第二个(右)加载到HashMap中。迭代的工作原理如下:

  1. 左迭代器中的项目与地图匹配 - 如果匹配,则返回两个项目
  2. 当左迭代器用尽时,返回地图中不匹配的值
  3. 问题是第二部分,我试图将地图的值迭代器与地图一起存储以存储其迭代状态。但正如我在answer中所了解的那样,生锈是不可能的。 不幸的是,我不知道如何做到这一点。

    以下是INNER JOIN适配器的完整代码,它执行第一部分:

    use std::collections::HashMap;
    use std::hash::Hash;
    
    pub struct HashJoinInner<I, K, V0, V1> where
        I: Iterator<Item=(K, V0)>,
        K: Hash + Eq,
        V1: Clone,
    {
        left: I,
        right: HashMap<K, V1>,
    }
    
    impl<I, K, V0, V1> HashJoinInner<I, K, V0, V1> where
        I: Iterator<Item=(K, V0)>,
        K: Hash + Eq,
        V1: Clone,
    {
        /// Create a `HashJoinInner` iterator.
        pub fn new<J>(l: I, r: J) -> Self
            where J: Iterator<Item=(K, V1)>
        {
            let mut hm: HashMap<K, V1> = HashMap::new();
            for (k, v) in r {
                hm.insert(k, v);
            }
            HashJoinInner {
                left: l,
                right: hm,
            }
        }
    }
    
    impl<I, K, V0, V1> Iterator for HashJoinInner<I, K, V0, V1> where
        I: Iterator<Item=(K, V0)>,
        K: Hash + Eq,
        V1: Clone,
    {
        type Item = (V0, V1);
    
        fn next(&mut self) -> Option<Self::Item> {
            loop {
                match self.left.next() {
                    Some((k0, v0)) => match self.right.get(&k0) {
                        Some(v1) => return Some((v0, Clone::clone(v1))),
                        None => continue,
                    },
                    None => return None,
                }
            }
        }
    }
    

    我会感激任何想法。

2 个答案:

答案 0 :(得分:1)

您无法存储Values迭代器,因为它包含对HashMap的引用。如果移动地图,这些引用可能会变为无效。但是,您可以使用HashMap方法使用 into_iter。它拥有HashMap的所有值,可以移动到新的结构中。

这是对前一个问题的代码的调整。这还不是左或右连接。从使用一个迭代器到完成另一个迭代器的转换很复杂。

use std::collections::hash_map::{HashMap, IntoIter};
use std::hash::Hash;

struct Foo<K, V>
    where K: Hash + Eq,
          V: Clone,
{
    iter: IntoIter<K, (V, bool)>,
}

impl<K, V> Foo<K, V>
    where K: Hash + Eq,
          V: Clone,
{
    fn new<I>(it: I) -> Self
        where I: Iterator<Item=(K, V)>
    {
        let mut map = HashMap::new();
        for (k, v) in it {
            map.insert(k, (v, false));
        }
        Foo { iter: map.into_iter() }
    }
}

impl<K, V> Iterator for Foo<K, V>
    where K: Hash + Eq,
          V: Clone,
{
    type Item = V;
    fn next(&mut self) -> Option<Self::Item> {
        loop {
            match self.iter.next() {
                Some((_, (v, false))) => return Some(v.clone()),
                Some(_) => continue,
                None => return None,
            }
        }
    }
}

fn main() {
    let it = (0..).zip("AB".chars());
    let foo = Foo::new(it);
    for v in foo {
        println!("{}", v);
    }
}

然而您无需执行任何操作即可获得所需内容。您可以简单地创建一个hashmap,并在迭代其他项时进行检查。我不小心创建了一个左外连接,但只是翻转参数以获得正确的外连接。 ^ _ ^

use std::collections::hash_map::HashMap;
use std::hash::Hash;

struct LeftOuterJoin<L, K, RV> {
    left: L,
    right: HashMap<K, RV>,
}

impl<L, K, RV> LeftOuterJoin<L, K, RV> 
    where K: Hash + Eq
{
    fn new<LI, RI>(left: LI, right: RI) -> Self
        where L: Iterator<Item=LI::Item>,
              LI: IntoIterator<IntoIter=L>,
              RI: IntoIterator<Item=(K, RV)>
    {
        LeftOuterJoin {
            left: left.into_iter(),
            right: right.into_iter().collect()
        }
    }
}

impl<L, K, LV, RV> Iterator for LeftOuterJoin<L, K, RV>
    where L: Iterator<Item=(K, LV)>,
          K: Hash + Eq,
          RV: Clone
{
    type Item = (K, LV, Option<RV>);

    fn next(&mut self) -> Option<Self::Item> {
        match self.left.next() {
            Some((k, lv)) => {
                let rv = self.right.get(&k);
                Some((k, lv, rv.cloned()))
            },
            None => None,
        }
    }
}

fn main() {
    let mut left = HashMap::new();
    left.insert(1, "Alice");
    left.insert(2, "Bob");

    let mut right = HashMap::new();
    right.insert(1, "Programmer");

    for (id, name, job) in LeftOuterJoin::new(left.into_iter(), right) {
        println!("{} ({}) is a {:?}", name, id, job);
    }
}

答案 1 :(得分:0)

感谢Shepmaster使用std::collections::hash_map::IntoIter的想法,我设法解决了这个问题。

以下是使用散列连接策略的RIGHT OUTER JOIN的完整解决方案:

use std::collections::hash_map::{HashMap, IntoIter,};
use std::mem;
use std::hash::Hash;

#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
pub struct HashJoinRightOuter<L, K, RV> {
    left: L,
    map: HashMap<K, (RV, bool)>,
    /// exclusion iterator - yields the unmatched values from the map. It is created once the left
    /// iterator is exhausted
    excl_iter: Option<IntoIter<K, (RV, bool)>>,
}

impl<L, K, RV> HashJoinRightOuter<L, K, RV> 
    where K: Hash + Eq,
{
    /// Create a `HashJoinRightOuter` iterator.
    pub fn new<LI, RI>(left: LI, right: RI) -> Self
        where L: Iterator<Item=LI::Item>,
              LI: IntoIterator<IntoIter=L>,
              RI: IntoIterator<Item=(K, RV)>
    {
        let mut map: HashMap<K, (RV, bool)> = HashMap::new();
        for (k, v) in right.into_iter() {
            map.insert(k, (v, false));
        }
        HashJoinRightOuter {
            left: left.into_iter(),
            map: map,
            excl_iter: None,
        }
    }

    /// Moves the map to `self.excl_iter`
    ///
    /// Once the left iterator is exhausted, the info about which keys were matched is complete.
    /// To be able to iterate over map's values we need to move it into its `IntoIter`.
    fn set_excl_iter(&mut self) {
        let map = mem::replace(&mut self.map, HashMap::<K, (RV, bool)>::new());
        self.excl_iter = Some(map.into_iter());
    }
}

impl<L, K, LV, RV> Iterator for HashJoinRightOuter<L, K, RV> 
    where L: Iterator<Item=(K, LV)>,
          K: Hash + Eq,
          RV: Clone,
{
    type Item = (Option<LV>, RV);

    fn next(&mut self) -> Option<Self::Item> {
        loop {
            match self.excl_iter {
                // the left iterator is not yet exhausted
                None => match self.left.next() {
                    Some((lk, lv)) => match self.map.get_mut(&lk) {
                        Some(rt) => {
                            rt.1 = true; // flag as matched
                            return Some((Some(lv), Clone::clone(&rt.0)))
                        },
                        None => continue, // not interested in unmatched left value
                    },
                    // the left iterator is exhausted so move the map into `self.excl_iter`.
                    None => self.set_excl_iter(),
                },
                // iterate over unmatched values
                Some(ref mut r) => match r.next() {
                    Some((_, (rv, matched))) => {
                        if !matched {
                            return Some((None, rv));
                        } else {
                            continue;
                        }
                    },
                    None => return None,
                }
            }
        }
    }
}

fn main() {
    let a = (0..).zip("AB".chars());
    let b = (1..).zip("XY".chars());
    let mut it = HashJoinRightOuter::new(a, b);
    assert_eq!(it.next(), Some((Some('B'), 'X')));
    assert_eq!(it.next(), Some((None, 'Y')));
    assert_eq!(it.next(), None);
}

一开始我失败了,因为我试图在相同的结构中存储数据和它的引用,这无论如何都没有意义。我真正想要的是首先存储数据,用它做一些魔术,一旦完成,移动到另一个字段以便进行转换。

这也可用于解决其他自引用结构问题。