Skip to content

Commit

Permalink
Make map field private. Impl Deref and DerefMut to the inner Map.
Browse files Browse the repository at this point in the history
  • Loading branch information
coriolinus committed Dec 11, 2017
1 parent 1337107 commit 10f6522
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 36 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "counter"
version = "0.1.0"
version = "0.2.0"
authors = ["Peter Goodspeed-Niklaus <peter.r.goodspeedniklaus@gmail.com>"]
description = "Simple package to count generic iterables"
repository = "https://github.com/coriolinus/counter-rs"
Expand Down
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

Simple counter library for Rust iterables. Inspired by, and largely mimicing the API of, Python's [Counter](https://docs.python.org/3.5/library/collections.html#collections.Counter).

Too tired for proper documentation at the moment; see the source. It's not really that long.

## Examples

### Get the most common characters in a string, breaking ties alphabetically
Expand All @@ -29,13 +27,15 @@ let expected = vec![('c', 3), ('d', 2), ('b', 2), ('e', 1), ('a', 1)];
assert!(by_common == expected);
```

### Directly modify the backing map
### Treat it like a Map

This is backed by a map to which you have full access:
`Counter<T>` implements `Deref<Target=HashMap<T, usize>>` and
`DerefMut<Target=HashMap<T, usize>>`, which means that you can perform any operations
on it which are valid for a [`HashMap`](https://doc.rust-lang.org/std/collections/struct.HashMap.html).

```rust
let mut counter = Counter::init("aa-bb-cc".chars());
counter.map.remove(&'-');
counter.remove(&'-');
assert!(counter == Counter::init("aabbcc".chars()));
```

Expand Down Expand Up @@ -70,7 +70,7 @@ let intys = vec![
];

let inty_counts = Counter::init(intys);
println!("{:?}", inty_counts.map);
println!("{:?}", inty_counts.map);
// {Inty { i: 8 }: 2, Inty { i: 0 }: 3, Inty { i: 9 }: 1, Inty { i: 3 }: 1,
// Inty { i: 7 }: 1, Inty { i: 6 }: 1, Inty { i: 5 }: 1}
assert!(inty_counts.map.get(&Inty { i: 8 }) == Some(&2));
Expand Down
86 changes: 57 additions & 29 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,18 @@
use std::collections::HashMap;
use std::hash::Hash;

use std::ops::{Add, Sub, BitAnd, BitOr};
use std::ops::{Add, Sub, BitAnd, BitOr, Deref, DerefMut};

type CounterMap<T> = HashMap<T, usize>;

#[derive(Clone, PartialEq, Eq)]
pub struct Counter<T: Hash + Eq> {
/// HashMap backing this Counter
///
/// Public to expose the HashMap API for direct manipulation.
/// That said, this may change in the future to some other mapping type / trait.
pub map: HashMap<T, usize>,
map: CounterMap<T>,
}

impl<T> Counter<T>
where T: Hash + Eq
where
T: Hash + Eq,
{
/// Create a new, empty `Counter`
pub fn new() -> Counter<T> {
Expand All @@ -27,7 +26,8 @@ impl<T> Counter<T>

/// Create a new `Counter` initialized with the given iterable
pub fn init<I>(iterable: I) -> Counter<T>
where I: IntoIterator<Item = T>
where
I: IntoIterator<Item = T>,
{
let mut counter = Counter::new();
counter.update(iterable);
Expand All @@ -36,7 +36,8 @@ impl<T> Counter<T>

/// Add the counts of the elements from the given iterable to this counter
pub fn update<I>(&mut self, iterable: I)
where I: IntoIterator<Item = T>
where
I: IntoIterator<Item = T>,
{
for item in iterable.into_iter() {
let entry = self.map.entry(item).or_insert(0);
Expand All @@ -48,7 +49,8 @@ impl<T> Counter<T>
///
/// Non-positive counts are automatically removed
pub fn subtract<I>(&mut self, iterable: I)
where I: IntoIterator<Item = T>
where
I: IntoIterator<Item = T>,
{
for item in iterable.into_iter() {
let mut remove = false;
Expand All @@ -66,7 +68,8 @@ impl<T> Counter<T>
}

impl<T> Counter<T>
where T: Hash + Eq + Clone
where
T: Hash + Eq + Clone,
{
/// Create an iterator over `(frequency, elem)` pairs, sorted most to least common.
///
Expand All @@ -90,24 +93,28 @@ impl<T> Counter<T>
/// to create some kind of MostCommon struct which implements `Iterator` which
/// does all the necessary work on demand. PRs appreciated here!
pub fn most_common_tiebreaker<F>(&self, tiebreaker: F) -> ::std::vec::IntoIter<(T, usize)>
where F: Fn(&T, &T) -> ::std::cmp::Ordering
where
F: Fn(&T, &T) -> ::std::cmp::Ordering,
{
use std::cmp::Ordering;

let mut items = self.map
.iter()
.map(|(key, &count)| (key.clone(), count))
.collect::<Vec<_>>();
items.sort_by(|&(ref a_item, a_count), &(ref b_item, b_count)| match b_count.cmp(&a_count) {
Ordering::Equal => tiebreaker(&a_item, &b_item),
unequal @ _ => unequal,
items.sort_by(|&(ref a_item, a_count), &(ref b_item, b_count)| {
match b_count.cmp(&a_count) {
Ordering::Equal => tiebreaker(&a_item, &b_item),
unequal @ _ => unequal,
}
});
items.into_iter()
}
}

impl<T> Counter<T>
where T: Hash + Eq + Clone + Ord
where
T: Hash + Eq + Clone + Ord,
{
/// Create an iterator over `(frequency, elem)` pairs, sorted most to least common.
///
Expand All @@ -124,7 +131,8 @@ impl<T> Counter<T>
}

impl<T> Add for Counter<T>
where T: Clone + Hash + Eq
where
T: Clone + Hash + Eq,
{
type Output = Counter<T>;

Expand All @@ -142,7 +150,8 @@ impl<T> Add for Counter<T>
}

impl<T> Sub for Counter<T>
where T: Clone + Hash + Eq
where
T: Clone + Hash + Eq,
{
type Output = Counter<T>;

Expand Down Expand Up @@ -172,7 +181,8 @@ impl<T> Sub for Counter<T>
}

impl<T> BitAnd for Counter<T>
where T: Clone + Hash + Eq
where
T: Clone + Hash + Eq,
{
type Output = Counter<T>;

Expand All @@ -189,16 +199,19 @@ impl<T> BitAnd for Counter<T>

let mut counter = Counter::new();
for key in both_keys {
counter.map.insert((*key).clone(),
min(*self.map.get(*key).unwrap(), *rhs.map.get(*key).unwrap()));
counter.map.insert(
(*key).clone(),
min(*self.map.get(*key).unwrap(), *rhs.map.get(*key).unwrap()),
);
}

counter
}
}

impl<T> BitOr for Counter<T>
where T: Clone + Hash + Eq
where
T: Clone + Hash + Eq,
{
type Output = Counter<T>;

Expand All @@ -217,6 +230,19 @@ impl<T> BitOr for Counter<T>
}
}

impl<T: Hash + Eq> Deref for Counter<T> {
type Target = CounterMap<T>;
fn deref(&self) -> &CounterMap<T> {
&self.map
}
}

impl<T: Hash + Eq> DerefMut for Counter<T> {
fn deref_mut(&mut self) -> &mut CounterMap<T> {
&mut self.map
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand All @@ -238,10 +264,8 @@ mod tests {
#[test]
fn test_update() {
let mut counter = Counter::init("abbccc".chars());
let expected: HashMap<char, usize> = [('a', 1), ('b', 2), ('c', 3)]
.iter()
.cloned()
.collect();
let expected: HashMap<char, usize> =
[('a', 1), ('b', 2), ('c', 3)].iter().cloned().collect();
assert!(counter.map == expected);

counter.update("aeeeee".chars());
Expand Down Expand Up @@ -271,15 +295,19 @@ mod tests {
#[test]
fn test_most_common_tiebreaker() {
let counter = Counter::init("eaddbbccc".chars());
let by_common = counter.most_common_tiebreaker(|&a, &b| a.cmp(&b)).collect::<Vec<_>>();
let by_common = counter
.most_common_tiebreaker(|&a, &b| a.cmp(&b))
.collect::<Vec<_>>();
let expected = vec![('c', 3), ('b', 2), ('d', 2), ('a', 1), ('e', 1)];
assert!(by_common == expected);
}

#[test]
fn test_most_common_tiebreaker_reversed() {
let counter = Counter::init("eaddbbccc".chars());
let by_common = counter.most_common_tiebreaker(|&a, &b| b.cmp(&a)).collect::<Vec<_>>();
let by_common = counter
.most_common_tiebreaker(|&a, &b| b.cmp(&a))
.collect::<Vec<_>>();
let expected = vec![('c', 3), ('d', 2), ('b', 2), ('e', 1), ('a', 1)];
assert!(by_common == expected);
}
Expand Down Expand Up @@ -335,7 +363,7 @@ mod tests {
#[test]
fn test_delete_key_from_backing_map() {
let mut counter = Counter::init("aa-bb-cc".chars());
counter.map.remove(&'-');
counter.remove(&'-');
assert!(counter == Counter::init("aabbcc".chars()));
}

Expand Down

0 comments on commit 10f6522

Please sign in to comment.