diff --git a/Cargo.lock b/Cargo.lock index 85d0f1d..25f7da2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,4 +1,5 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. [[package]] name = "linq" version = "0.0.1-release" - diff --git a/README.md b/README.md index 4d70f3c..6fed27d 100644 --- a/README.md +++ b/README.md @@ -86,8 +86,8 @@ The trait `linq::Queryable` supports LINQ methods on `Iterator`. You can find th - [ ] *then_by_descending* - [x] **reverse** => rev - [ ] *group_by* -- [ ] *distinct* -- [ ] *union* +- [x] distinct +- [x] union - [ ] *intersect* - [ ] *except* - [x] **first** => next diff --git a/src/iter/m_distinct.rs b/src/iter/m_distinct.rs new file mode 100644 index 0000000..48bc3a0 --- /dev/null +++ b/src/iter/m_distinct.rs @@ -0,0 +1,48 @@ +#[derive(Clone)] +pub struct DistinctIterator +where + I::Item : Eq + std::hash::Hash + Copy, + I : Iterator +{ + source : I, + hash_map : std::collections::HashSet +} + +impl Iterator for DistinctIterator +where + I : Iterator, + I::Item : Eq + std::hash::Hash + Copy, +{ + type Item = I::Item; + + fn next(&mut self) -> Option { + loop { + match self.source.next() { + Some(item) => { + if self.hash_map.insert(item) + { + return Some(item); + } + } + None => { + return None; + } + } + } + } +} + + +pub fn distinct( + iter: I +) -> DistinctIterator +where + I : Iterator, + I::Item : Eq + std::hash::Hash + Copy, +{ + let hash_map = std::collections::HashSet::new(); + DistinctIterator { + hash_map : hash_map, + source : iter + } +} diff --git a/src/iter/m_enumerable.rs b/src/iter/m_enumerable.rs index 992265c..0c2f351 100644 --- a/src/iter/m_enumerable.rs +++ b/src/iter/m_enumerable.rs @@ -1,4 +1,4 @@ -use super::{m_builtin, m_method, m_order_by, m_select}; +use super::{m_builtin, m_method, m_order_by, m_select, m_distinct, m_union}; use m_builtin::{ConcateIterator, ReverseIterator, SelectIterator, WhereIterator}; use m_order_by::OrderedIterator; use m_select::{SelectManyIterator, SelectManySingleIterator}; @@ -302,6 +302,24 @@ pub trait Enumerable: Iterator { { m_builtin::aggregate(self, init, f) } + + fn distinct(self) -> m_distinct::DistinctIterator + where + Self: Sized, + Self::Item: Eq + std::hash::Hash + Copy, + { + m_distinct::distinct(self) + } + + fn union(self, union_with : U) -> m_union::UnionIterator + where + Self: Sized, + Self::Item: Eq + std::hash::Hash + Copy, + U: Enumerable, + { + m_union::union(self, union_with) + } + } impl Enumerable for I where I: Iterator {} diff --git a/src/iter/m_union.rs b/src/iter/m_union.rs new file mode 100644 index 0000000..ce8c804 --- /dev/null +++ b/src/iter/m_union.rs @@ -0,0 +1,70 @@ +#[derive(Clone)] +pub struct UnionIterator +where + I::Item : Eq + std::hash::Hash + Copy, + I : Iterator, + U : Iterator, +{ + first_source : I, + second_source : U, + was_first_source_consumed : bool, + hash_map : std::collections::HashSet +} + +impl Iterator for UnionIterator +where + I : Iterator, + U : Iterator, + I::Item : Eq + std::hash::Hash + Copy, +{ + type Item = I::Item; + + fn next(&mut self) -> Option { + loop { + if !self.was_first_source_consumed + { + match self.first_source.next() { + Some(item) => { + if self.hash_map.insert(item) + { + return Some(item); + } + } + None => { + self.was_first_source_consumed = true; + } + } + } + match self.second_source.next() { + Some(item) => { + if self.hash_map.insert(item) + { + return Some(item); + } + } + None => { + return None; + } + } + } + } +} + + +pub fn union( + iter: I, + iter_union : U +) -> UnionIterator +where + I : Iterator, + U : Iterator, + I::Item : Eq + std::hash::Hash + Copy, +{ + let hash_map = std::collections::HashSet::new(); + UnionIterator { + hash_map : hash_map, + first_source : iter, + second_source : iter_union, + was_first_source_consumed : false + } +} diff --git a/src/iter/mod.rs b/src/iter/mod.rs index bf2ad44..eede593 100644 --- a/src/iter/mod.rs +++ b/src/iter/mod.rs @@ -5,5 +5,7 @@ mod m_order_by; mod m_select; mod m_builtin; mod m_method; +mod m_distinct; +mod m_union; pub use m_enumerable::*; diff --git a/src/lib.rs b/src/lib.rs index 2a7c175..aab13bc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -33,34 +33,66 @@ macro_rules! linq { { $c.select(|$v| $ms) }; + (from $v:ident in $c:expr, select distinct $ms:expr) => + { + $c.select(|$v| $ms).distinct() + }; (from $v:ident in $c:expr, $(where $mw:expr,)+ select $ms:expr) => { $c.where_by(|$v| true $(&& $mw)+ ).select(|$v| $ms) }; + (from $v:ident in $c:expr, $(where $mw:expr,)+ select distinct $ms:expr) => + { + $c.where_by(|$v| true $(&& $mw)+ ).select(|$v| $ms).distinct() + }; (from $v:ident in $c:expr, orderby $mo:expr, select $ms:expr) => { $c.order_by(|$v| $mo).select(|$v| $ms) }; + (from $v:ident in $c:expr, orderby $mo:expr, select distinct $ms:expr) => + { + $c.order_by(|$v| $mo).select(|$v| $ms).distinct() + }; (from $v:ident in $c:expr, orderby $mo:expr, descending, select $ms:expr) => { $c.order_by_descending(|$v| $mo).select(|$v| $ms) }; + (from $v:ident in $c:expr, orderby $mo:expr, descending, select distinct $ms:expr) => + { + $c.order_by_descending(|$v| $mo).select(|$v| $ms).distinct() + }; (from $v:ident in $c:expr, $(where $mw:expr,)+ orderby $mo:expr, select $ms:expr) => { $c.where_by(|$v| true $(&& $mw)+ ).order_by(|$v| $mo).select(|$v| $ms) }; + (from $v:ident in $c:expr, $(where $mw:expr,)+ orderby $mo:expr, select distinct $ms:expr) => + { + $c.where_by(|$v| true $(&& $mw)+ ).order_by(|$v| $mo).select(|$v| $ms).distinct() + }; (from $v:ident in $c:expr, $(where $mw:expr,)+ orderby $mo:expr, descending, select $ms:expr) => { $c.where_by(|$v| true $(&& $mw)+ ).order_by_descending(|$v| $mo).select(|$v| $ms) }; + (from $v:ident in $c:expr, $(where $mw:expr,)+ orderby $mo:expr, descending, select distinct $ms:expr) => + { + $c.where_by(|$v| true $(&& $mw)+ ).order_by_descending(|$v| $mo).select(|$v| $ms).distinct() + }; (from $v0:ident in $c0:expr, from $v:ident in $c:expr, select $ms:expr) => { $c0.select_many_single(|$v0| $c).select(|$v| $ms) }; + (from $v0:ident in $c0:expr, from $v:ident in $c:expr, select distinct $ms:expr) => + { + $c0.select_many_single(|$v0| $c).select(|$v| $ms).distinct() + }; (from $v0:ident in $c0:expr, zfrom $v:ident in $c:expr, select $ms:expr) => { $c0.select_many(|$v0| $c, |$v0, $v| $ms) }; + (from $v0:ident in $c0:expr, zfrom $v:ident in $c:expr, select distinct $ms:expr) => + { + $c0.select_many(|$v0| $c, |$v0, $v| $ms).distinct() + }; } #[cfg(test)] diff --git a/src/tests/iter_expr.rs b/src/tests/iter_expr.rs index 54ac9d0..dd30f87 100644 --- a/src/tests/iter_expr.rs +++ b/src/tests/iter_expr.rs @@ -81,3 +81,11 @@ fn where_order() { let e: Vec = linq!(from p in x.clone(), where p <= &5, orderby -p, select p * 2).collect(); assert_eq!(e, y); } + +#[test] +fn distinct() { + let x = [1, 2, 4, 2, 5, 6]; + let y: Vec = x.iter().distinct().cloned().collect(); + let e: Vec = linq!(from p in x.iter(), select distinct p).cloned().collect(); + assert_eq!(e, y); +} \ No newline at end of file diff --git a/src/tests/iter_methods.rs b/src/tests/iter_methods.rs index 1037650..22ef523 100644 --- a/src/tests/iter_methods.rs +++ b/src/tests/iter_methods.rs @@ -140,4 +140,31 @@ fn element_at() { assert_eq!(a.iter().element_at(1), Some(&2)); assert_eq!(a.iter().element_at(2), Some(&3)); assert_eq!(a.iter().element_at(3), None); +} + +#[test] +fn distict() { + let a = [1, 2, 3, 2, 3, 5]; + let mut iter = a.iter().distinct(); + assert_eq!(iter.next(), Some(&1)); + assert_eq!(iter.next(), Some(&2)); + assert_eq!(iter.next(), Some(&3)); + assert_eq!(iter.next(), Some(&5)); + + assert_eq!(iter.next(), None); +} + +#[test] +fn union() { + let a = [1, 2, 3, 2, 3, 4]; + let b = [1, 2, 2, 5, 3, 6]; + let mut iter = a.iter().union(b.iter()); + assert_eq!(iter.next(), Some(&1)); + assert_eq!(iter.next(), Some(&2)); + assert_eq!(iter.next(), Some(&3)); + assert_eq!(iter.next(), Some(&4)); + assert_eq!(iter.next(), Some(&5)); + assert_eq!(iter.next(), Some(&6)); + + assert_eq!(iter.next(), None); } \ No newline at end of file