// Better grouping in Rust. My first attempt leaked the type of the test function which made reuse harder.
mod group {
use core::slice::Iter;
pub struct GroupByIter<'a, E: 'a, G: Eq + 'a> {
iter: Iter<'a, E>,
test: Box<dyn Fn(&'a E) -> G>,
curr: Option<G>,
last: Option<&'a E>
}
impl<'a, E: 'a, G: Eq + 'a> GroupByIter<'a, E, G> {
fn new<F: 'static>(slice: &'a [E], test: F) -> GroupByIter<'a, E, G> where F: Fn(&'a E) -> G {
GroupByIter { iter: slice.iter(), test: Box::new(test), curr: None, last: None }
}
}
impl<'a, E: 'a, G: Eq + 'a> Iterator for GroupByIter<'a, E, G> {
type Item = Vec<&'a E>;
fn next(&mut self) -> Option<Self::Item> {
let mut group = Vec::new();
if let Some(e) = self.last {
group.push(e);
self.last = None;
}
let test = &self.test;
loop {
match self.iter.next() {
None => break,
Some(e) => {
let new = Some(test(e));
if self.curr == new || self.curr.is_none() {
group.push(e);
self.curr = new;
} else {
self.last = Some(e);
self.curr = new;
break;
}
}
}
}
if !group.is_empty() { Some(group) } else { None }
}
}
pub trait GroupBy {
type Item;
fn group_by<'a, F: 'static, G: Eq + 'a>(&'a self, test: F) -> GroupByIter<'a, Self::Item, G> where F: Fn(&'a Self::Item) -> G;
fn group<'a>(&'a self) -> GroupByIter<'a, Self::Item, &'a Self::Item> where Self::Item: Eq + 'a {
self.group_by(|e| e)
}
}
impl<E> GroupBy for [E] {
type Item = E;
fn group_by<'a, F: 'static, G: Eq + 'a>(&'a self, test: F) -> GroupByIter<'a, E, G> where E: 'a, F: Fn(&'a E) -> G {
GroupByIter::new(self, test)
}
}
}
Comments
0 B
|👍
/👎
0 B
|👍
/👎