diff --git a/Cargo.toml b/Cargo.toml index 82aed47..3237b78 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "counter" -version = "0.3.3" +version = "0.4.0" authors = ["Peter Goodspeed-Niklaus "] description = "Simple package to count generic iterables" repository = "https://github.com/coriolinus/counter-rs" @@ -10,3 +10,4 @@ keywords = ["count"] license = "MIT" [dependencies] +num-traits = "0.2" diff --git a/src/lib.rs b/src/lib.rs index 82b486d..342a431 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,31 +3,35 @@ //! //! Counts recurring elements from an iterable. +extern crate num_traits; +use num_traits::{One, Zero}; + use std::collections::HashMap; use std::hash::Hash; use std::iter; use std::ops::{Add, AddAssign, BitAnd, BitOr, Deref, DerefMut, Sub, SubAssign}; -type CounterMap = HashMap; +type CounterMap = HashMap; #[derive(Clone, PartialEq, Eq, Debug, Default)] -pub struct Counter { - map: CounterMap, +pub struct Counter { + map: CounterMap, } -impl Counter +impl Counter where T: Hash + Eq, + N: Clone + PartialOrd + AddAssign + SubAssign + Zero + One, { /// Create a new, empty `Counter` - pub fn new() -> Counter { + pub fn new() -> Counter { Counter { map: HashMap::new(), } } /// Create a new `Counter` initialized with the given iterable - pub fn init(iterable: I) -> Counter + pub fn init(iterable: I) -> Counter where I: IntoIterator, { @@ -42,8 +46,8 @@ where I: IntoIterator, { for item in iterable.into_iter() { - let entry = self.map.entry(item).or_insert(0); - *entry += 1; + let entry = self.map.entry(item).or_insert(N::zero()); + *entry += N::one(); } } @@ -57,10 +61,10 @@ where for item in iterable.into_iter() { let mut remove = false; if let Some(entry) = self.map.get_mut(&item) { - if *entry > 0 { - *entry -= 1; + if *entry > N::zero() { + *entry -= N::one(); } - remove = *entry == 0; + remove = *entry == N::zero(); } if remove { self.map.remove(&item); @@ -69,12 +73,13 @@ where } } -impl Counter +impl Counter where T: Hash + Eq + Clone, + N: Clone + Copy + Ord, { /// Create an iterator over `(frequency, elem)` pairs, sorted most to least common. - pub fn most_common(&self) -> Vec<(T, usize)> { + pub fn most_common(&self) -> Vec<(T, N)> { use std::cmp::Ordering; self.most_common_tiebreaker(|ref _a, ref _b| Ordering::Equal) } @@ -83,7 +88,7 @@ where /// /// In the event that two keys have an equal frequency, use the supplied ordering function /// to further arrange the results. - pub fn most_common_tiebreaker(&self, tiebreaker: F) -> Vec<(T, usize)> + pub fn most_common_tiebreaker(&self, tiebreaker: F) -> Vec<(T, N)> where F: Fn(&T, &T) -> ::std::cmp::Ordering, { @@ -103,58 +108,62 @@ where } } -impl Counter +impl Counter where T: Hash + Eq + Clone + Ord, + N: Clone + Copy + Ord, { /// Create an iterator over `(frequency, elem)` pairs, sorted most to least common. /// /// In the event that two keys have an equal frequency, use the natural ordering of the keys /// to further sort the results. - pub fn most_common_ordered(&self) -> Vec<(T, usize)> { + pub fn most_common_ordered(&self) -> Vec<(T, N)> { self.most_common_tiebreaker(|ref a, ref b| a.cmp(&b)) } } -impl AddAssign for Counter +impl AddAssign for Counter where T: Clone + Hash + Eq, + N: Clone + Copy + Zero + AddAssign, { /// Add another counter to this counter /// /// `c += d;` -> `c[x] += d[x]` for all `x` fn add_assign(&mut self, rhs: Self) { for (key, value) in rhs.map.iter() { - let entry = self.map.entry(key.clone()).or_insert(0); + let entry = self.map.entry(key.clone()).or_insert(N::zero()); *entry += *value; } } } -impl Add for Counter +impl Add for Counter where T: Clone + Hash + Eq, + N: Clone + Copy + PartialOrd + PartialEq + AddAssign + Zero, { - type Output = Counter; + type Output = Counter; /// Add two counters together. /// /// `out = c + d;` -> `out[x] == c[x] + d[x]` for all `x` - fn add(self, rhs: Counter) -> Counter { + fn add(self, rhs: Counter) -> Self::Output { let mut counter = self.clone(); counter += rhs; counter } } -impl SubAssign for Counter +impl SubAssign for Counter where T: Hash + Eq, + N: Clone + Copy + PartialOrd + PartialEq + SubAssign + Zero, { /// Subtract (keeping only positive values). /// /// `c -= d;` -> `c[x] -= d[x]` for all `x`, - /// keeping only items with a value greater than 0. + /// keeping only items with a value greater than N::zero(). fn sub_assign(&mut self, rhs: Self) { for (key, value) in rhs.map.iter() { let mut remove = false; @@ -164,7 +173,7 @@ where } else { remove = true; } - if *entry == 0 { + if *entry == N::zero() { remove = true; } } @@ -175,32 +184,34 @@ where } } -impl Sub for Counter +impl Sub for Counter where T: Hash + Eq, + N: Clone + Copy + PartialOrd + PartialEq + SubAssign + Zero, { - type Output = Counter; + type Output = Counter; /// Subtract (keeping only positive values). /// /// `out = c - d;` -> `out[x] == c[x] - d[x]` for all `x`, - /// keeping only items with a value greater than 0. - fn sub(mut self, rhs: Counter) -> Counter { + /// keeping only items with a value greater than N::zero(). + fn sub(mut self, rhs: Counter) -> Self::Output { self -= rhs; self } } -impl BitAnd for Counter +impl BitAnd for Counter where T: Clone + Hash + Eq, + N: Clone + Copy + Ord + AddAssign + SubAssign + Zero + One, { - type Output = Counter; + type Output = Counter; /// Intersection /// /// `out = c & d;` -> `out[x] == min(c[x], d[x])` - fn bitand(self, rhs: Counter) -> Counter { + fn bitand(self, rhs: Counter) -> Self::Output { use std::cmp::min; use std::collections::HashSet; @@ -220,44 +231,54 @@ where } } -impl BitOr for Counter +impl BitOr for Counter where T: Clone + Hash + Eq, + N: Clone + Copy + Ord + Zero, { - type Output = Counter; + type Output = Counter; /// Union /// /// `out = c | d;` -> `out[x] == max(c[x], d[x])` - fn bitor(self, rhs: Counter) -> Counter { + fn bitor(self, rhs: Counter) -> Self::Output { use std::cmp::max; let mut counter = self.clone(); for (key, value) in rhs.map.iter() { - let entry = counter.map.entry(key.clone()).or_insert(0); + let entry = counter.map.entry(key.clone()).or_insert(N::zero()); *entry = max(*entry, *value); } counter } } -impl Deref for Counter { - type Target = CounterMap; - fn deref(&self) -> &CounterMap { +impl Deref for Counter +where + T: Hash + Eq, + N: Clone, +{ + type Target = CounterMap; + fn deref(&self) -> &CounterMap { &self.map } } -impl DerefMut for Counter { - fn deref_mut(&mut self) -> &mut CounterMap { +impl DerefMut for Counter +where + T: Hash + Eq, + N: Clone, +{ + fn deref_mut(&mut self) -> &mut CounterMap { &mut self.map } } -impl AddAssign for Counter +impl AddAssign for Counter where - T: Hash + Eq, I: IntoIterator, + T: Hash + Eq, + N: Clone + PartialOrd + AddAssign + SubAssign + Zero + One, { /// Directly add the counts of the elements of `I` to `self` fn add_assign(&mut self, rhs: I) { @@ -265,22 +286,24 @@ where } } -impl Add for Counter +impl Add for Counter where - T: Hash + Eq, I: IntoIterator, + T: Hash + Eq, + N: Clone + PartialOrd + AddAssign + SubAssign + Zero + One, { - type Output = Counter; - fn add(mut self, rhs: I) -> Counter { + type Output = Self; + fn add(mut self, rhs: I) -> Self::Output { self.update(rhs); self } } -impl SubAssign for Counter +impl SubAssign for Counter where - T: Hash + Eq, I: IntoIterator, + T: Hash + Eq, + N: Clone + PartialOrd + AddAssign + SubAssign + Zero + One, { /// Directly subtract the counts of the elements of `I` from `self` fn sub_assign(&mut self, rhs: I) { @@ -288,39 +311,42 @@ where } } -impl Sub for Counter +impl Sub for Counter where - T: Clone + Hash + Eq, I: IntoIterator, + T: Clone + Hash + Eq, + N: Clone + PartialOrd + AddAssign + SubAssign + Zero + One, { - type Output = Counter; - fn sub(self, rhs: I) -> Counter { + type Output = Self; + fn sub(self, rhs: I) -> Self::Output { let mut ctr = self.clone(); ctr.subtract(rhs); ctr } } -impl iter::FromIterator for Counter +impl iter::FromIterator for Counter where T: Hash + Eq, + N: Clone + PartialOrd + AddAssign + SubAssign + Zero + One, { fn from_iter>(iter: I) -> Self { - Counter::init(iter) + Counter::::init(iter) } } -impl iter::FromIterator<(T, usize)> for Counter +impl iter::FromIterator<(T, N)> for Counter where T: Hash + Eq, + N: Clone + PartialOrd + AddAssign + SubAssign + Zero + One, { /// `from_iter` creates a counter from `(item, count)` tuples. /// /// The counts of duplicate items are summed. - fn from_iter>(iter: I) -> Self { + fn from_iter>(iter: I) -> Self { let mut cnt = Counter::new(); for (item, item_count) in iter.into_iter() { - let entry = cnt.map.entry(item).or_insert(0); + let entry = cnt.map.entry(item).or_insert(N::zero()); *entry += item_count; } cnt @@ -418,7 +444,7 @@ mod tests { #[test] fn test_composite_add_sub() { - let mut counts = Counter::init( + let mut counts = Counter::<_, usize>::init( "able babble table babble rabble table able fable scrabble".split_whitespace(), ); // add or subtract an iterable of the same type @@ -462,8 +488,8 @@ mod tests { #[test] fn test_add() { - let d = Counter::init("abbccc".chars()); - let e = Counter::init("bccddd".chars()); + let d = Counter::<_, usize>::init("abbccc".chars()); + let e = Counter::<_, usize>::init("bccddd".chars()); let out = d + e; let expected = Counter::init("abbbcccccddd".chars()); @@ -472,8 +498,8 @@ mod tests { #[test] fn test_sub() { - let d = Counter::init("abbccc".chars()); - let e = Counter::init("bccddd".chars()); + let d = Counter::<_, usize>::init("abbccc".chars()); + let e = Counter::<_, usize>::init("bccddd".chars()); let out = d - e; let expected = Counter::init("abc".chars()); @@ -482,8 +508,8 @@ mod tests { #[test] fn test_intersection() { - let d = Counter::init("abbccc".chars()); - let e = Counter::init("bccddd".chars()); + let d = Counter::<_, usize>::init("abbccc".chars()); + let e = Counter::<_, usize>::init("bccddd".chars()); let out = d & e; let expected = Counter::init("bcc".chars()); @@ -492,8 +518,8 @@ mod tests { #[test] fn test_union() { - let d = Counter::init("abbccc".chars()); - let e = Counter::init("bccddd".chars()); + let d = Counter::<_, usize>::init("abbccc".chars()); + let e = Counter::<_, usize>::init("bccddd".chars()); let out = d | e; let expected = Counter::init("abbcccddd".chars()); @@ -502,7 +528,7 @@ mod tests { #[test] fn test_delete_key_from_backing_map() { - let mut counter = Counter::init("aa-bb-cc".chars()); + let mut counter = Counter::<_, usize>::init("aa-bb-cc".chars()); counter.remove(&'-'); assert!(counter == Counter::init("aabbcc".chars())); } @@ -571,4 +597,19 @@ mod tests { assert!(inty_counts.map.get(&Inty { i: 0 }) == Some(&3)); assert!(inty_counts.map.get(&Inty { i: 6 }) == Some(&1)); } + + #[test] + fn test_collect() { + let counter: Counter<_> = "abbccc".chars().collect(); + let expected: HashMap = + [('a', 1), ('b', 2), ('c', 3)].iter().cloned().collect(); + assert!(counter.map == expected); + } + + #[test] + fn test_non_usize_count() { + let counter: Counter<_, i8> = "abbccc".chars().collect(); + let expected: HashMap = [('a', 1), ('b', 2), ('c', 3)].iter().cloned().collect(); + assert!(counter.map == expected); + } }