Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 40 additions & 25 deletions src/grouping_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ use crate::{
adaptors::map::{MapSpecialCase, MapSpecialCaseFn},
MinMaxResult,
};
use std::cmp::Ordering;
use core::hash::BuildHasher;
use std::collections::HashMap;
use std::hash::Hash;
use std::iter::Iterator;
use std::ops::{Add, Mul};
use std::{cmp::Ordering, hash::RandomState};

/// A wrapper to allow for an easy [`into_grouping_map_by`](crate::Itertools::into_grouping_map_by)
pub type MapForGrouping<I, F> = MapSpecialCase<I, GroupingMapFn<F>>;
Expand Down Expand Up @@ -36,18 +37,30 @@ pub(crate) fn new_map_for_grouping<K, I: Iterator, F: FnMut(&I::Item) -> K>(
}

/// Creates a new `GroupingMap` from `iter`
pub fn new<I, K, V>(iter: I) -> GroupingMap<I>
pub fn new<I, K, V>(iter: I) -> GroupingMap<I, RandomState>
where
I: Iterator<Item = (K, V)>,
K: Hash + Eq,
{
GroupingMap { iter }
let hash_builder = RandomState::new();
GroupingMap { iter, hash_builder }
}

/// Creates a new `GroupingMap` from `iter` which will use the given hash
/// builder to hash keys.
pub fn with_hasher<I, K, V, S>(iter: I, hash_builder: S) -> GroupingMap<I, S>
where
I: Iterator<Item = (K, V)>,
K: Hash + Eq,
S: BuildHasher,
{
GroupingMap { iter, hash_builder }
}

/// `GroupingMapBy` is an intermediate struct for efficient group-and-fold operations.
///
/// See [`GroupingMap`] for more informations.
pub type GroupingMapBy<I, F> = GroupingMap<MapForGrouping<I, F>>;
pub type GroupingMapBy<I, F, S> = GroupingMap<MapForGrouping<I, F>, S>;

/// `GroupingMap` is an intermediate struct for efficient group-and-fold operations.
/// It groups elements by their key and at the same time fold each group
Expand All @@ -56,14 +69,16 @@ pub type GroupingMapBy<I, F> = GroupingMap<MapForGrouping<I, F>>;
/// No method on this struct performs temporary allocations.
#[derive(Clone, Debug)]
#[must_use = "GroupingMap is lazy and do nothing unless consumed"]
pub struct GroupingMap<I> {
pub struct GroupingMap<I, S> {
iter: I,
hash_builder: S,
}

impl<I, K, V> GroupingMap<I>
impl<I, K, V, S> GroupingMap<I, S>
where
I: Iterator<Item = (K, V)>,
K: Hash + Eq,
S: BuildHasher,
{
/// This is the generic way to perform any operation on a `GroupingMap`.
/// It's suggested to use this method only to implement custom operations
Expand Down Expand Up @@ -105,11 +120,11 @@ where
/// assert_eq!(lookup[&3], 7);
/// assert_eq!(lookup.len(), 3); // The final keys are only 0, 1 and 2
/// ```
pub fn aggregate<FO, R>(self, mut operation: FO) -> HashMap<K, R>
pub fn aggregate<FO, R>(self, mut operation: FO) -> HashMap<K, R, S>
where
FO: FnMut(Option<R>, &K, V) -> Option<R>,
{
let mut destination_map = HashMap::new();
let mut destination_map = HashMap::with_hasher(self.hash_builder);

self.iter.for_each(|(key, val)| {
let acc = destination_map.remove(&key);
Expand Down Expand Up @@ -154,7 +169,7 @@ where
/// assert_eq!(lookup[&2].acc, 2 + 5);
/// assert_eq!(lookup.len(), 3);
/// ```
pub fn fold_with<FI, FO, R>(self, mut init: FI, mut operation: FO) -> HashMap<K, R>
pub fn fold_with<FI, FO, R>(self, mut init: FI, mut operation: FO) -> HashMap<K, R, S>
where
FI: FnMut(&K, &V) -> R,
FO: FnMut(R, &K, V) -> R,
Expand Down Expand Up @@ -190,7 +205,7 @@ where
/// assert_eq!(lookup[&2], 2 + 5);
/// assert_eq!(lookup.len(), 3);
/// ```
pub fn fold<FO, R>(self, init: R, operation: FO) -> HashMap<K, R>
pub fn fold<FO, R>(self, init: R, operation: FO) -> HashMap<K, R, S>
where
R: Clone,
FO: FnMut(R, &K, V) -> R,
Expand Down Expand Up @@ -225,7 +240,7 @@ where
/// assert_eq!(lookup[&2], 2 + 5);
/// assert_eq!(lookup.len(), 3);
/// ```
pub fn reduce<FO>(self, mut operation: FO) -> HashMap<K, V>
pub fn reduce<FO>(self, mut operation: FO) -> HashMap<K, V, S>
where
FO: FnMut(V, &K, V) -> V,
{
Expand All @@ -239,7 +254,7 @@ where

/// See [`.reduce()`](GroupingMap::reduce).
#[deprecated(note = "Use .reduce() instead", since = "0.13.0")]
pub fn fold_first<FO>(self, operation: FO) -> HashMap<K, V>
pub fn fold_first<FO>(self, operation: FO) -> HashMap<K, V, S>
where
FO: FnMut(V, &K, V) -> V,
{
Expand All @@ -264,11 +279,11 @@ where
/// assert_eq!(lookup[&2], vec![2, 5].into_iter().collect::<HashSet<_>>());
/// assert_eq!(lookup.len(), 3);
/// ```
pub fn collect<C>(self) -> HashMap<K, C>
pub fn collect<C>(self) -> HashMap<K, C, S>
where
C: Default + Extend<V>,
{
let mut destination_map = HashMap::new();
let mut destination_map = HashMap::with_hasher(self.hash_builder);

self.iter.for_each(|(key, val)| {
destination_map
Expand Down Expand Up @@ -298,7 +313,7 @@ where
/// assert_eq!(lookup[&2], 8);
/// assert_eq!(lookup.len(), 3);
/// ```
pub fn max(self) -> HashMap<K, V>
pub fn max(self) -> HashMap<K, V, S>
where
V: Ord,
{
Expand All @@ -324,7 +339,7 @@ where
/// assert_eq!(lookup[&2], 5);
/// assert_eq!(lookup.len(), 3);
/// ```
pub fn max_by<F>(self, mut compare: F) -> HashMap<K, V>
pub fn max_by<F>(self, mut compare: F) -> HashMap<K, V, S>
where
F: FnMut(&K, &V, &V) -> Ordering,
{
Expand Down Expand Up @@ -353,7 +368,7 @@ where
/// assert_eq!(lookup[&2], 5);
/// assert_eq!(lookup.len(), 3);
/// ```
pub fn max_by_key<F, CK>(self, mut f: F) -> HashMap<K, V>
pub fn max_by_key<F, CK>(self, mut f: F) -> HashMap<K, V, S>
where
F: FnMut(&K, &V) -> CK,
CK: Ord,
Expand All @@ -379,7 +394,7 @@ where
/// assert_eq!(lookup[&2], 5);
/// assert_eq!(lookup.len(), 3);
/// ```
pub fn min(self) -> HashMap<K, V>
pub fn min(self) -> HashMap<K, V, S>
where
V: Ord,
{
Expand All @@ -405,7 +420,7 @@ where
/// assert_eq!(lookup[&2], 8);
/// assert_eq!(lookup.len(), 3);
/// ```
pub fn min_by<F>(self, mut compare: F) -> HashMap<K, V>
pub fn min_by<F>(self, mut compare: F) -> HashMap<K, V, S>
where
F: FnMut(&K, &V, &V) -> Ordering,
{
Expand Down Expand Up @@ -434,7 +449,7 @@ where
/// assert_eq!(lookup[&2], 8);
/// assert_eq!(lookup.len(), 3);
/// ```
pub fn min_by_key<F, CK>(self, mut f: F) -> HashMap<K, V>
pub fn min_by_key<F, CK>(self, mut f: F) -> HashMap<K, V, S>
where
F: FnMut(&K, &V) -> CK,
CK: Ord,
Expand Down Expand Up @@ -469,7 +484,7 @@ where
/// assert_eq!(lookup[&2], OneElement(5));
/// assert_eq!(lookup.len(), 3);
/// ```
pub fn minmax(self) -> HashMap<K, MinMaxResult<V>>
pub fn minmax(self) -> HashMap<K, MinMaxResult<V>, S>
where
V: Ord,
{
Expand Down Expand Up @@ -499,7 +514,7 @@ where
/// assert_eq!(lookup[&2], OneElement(5));
/// assert_eq!(lookup.len(), 3);
/// ```
pub fn minmax_by<F>(self, mut compare: F) -> HashMap<K, MinMaxResult<V>>
pub fn minmax_by<F>(self, mut compare: F) -> HashMap<K, MinMaxResult<V>, S>
where
F: FnMut(&K, &V, &V) -> Ordering,
{
Expand Down Expand Up @@ -550,7 +565,7 @@ where
/// assert_eq!(lookup[&2], OneElement(5));
/// assert_eq!(lookup.len(), 3);
/// ```
pub fn minmax_by_key<F, CK>(self, mut f: F) -> HashMap<K, MinMaxResult<V>>
pub fn minmax_by_key<F, CK>(self, mut f: F) -> HashMap<K, MinMaxResult<V>, S>
where
F: FnMut(&K, &V) -> CK,
CK: Ord,
Expand All @@ -577,7 +592,7 @@ where
/// assert_eq!(lookup[&2], 5 + 8);
/// assert_eq!(lookup.len(), 3);
/// ```
pub fn sum(self) -> HashMap<K, V>
pub fn sum(self) -> HashMap<K, V, S>
where
V: Add<V, Output = V>,
{
Expand All @@ -603,7 +618,7 @@ where
/// assert_eq!(lookup[&2], 5 * 8);
/// assert_eq!(lookup.len(), 3);
/// ```
pub fn product(self) -> HashMap<K, V>
pub fn product(self) -> HashMap<K, V, S>
where
V: Mul<V, Output = V>,
{
Expand Down
53 changes: 50 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ use std::fmt;
#[cfg(feature = "use_alloc")]
use std::fmt::Write;
#[cfg(feature = "use_std")]
use std::hash::Hash;
use std::hash::{BuildHasher, Hash, RandomState};
use std::iter::{once, IntoIterator};
#[cfg(feature = "use_alloc")]
type VecDequeIntoIter<T> = alloc::collections::vec_deque::IntoIter<T>;
Expand Down Expand Up @@ -3800,14 +3800,34 @@ pub trait Itertools: Iterator {
/// See [`GroupingMap`] for more informations
/// on what operations are available.
#[cfg(feature = "use_std")]
fn into_grouping_map<K, V>(self) -> GroupingMap<Self>
fn into_grouping_map<K, V>(self) -> GroupingMap<Self, RandomState>
where
Self: Iterator<Item = (K, V)> + Sized,
K: Hash + Eq,
{
grouping_map::new(self)
}

/// Constructs a `GroupingMap`, which will use the given hash builder to
/// hash keys, to be used later with one of the efficient group-and-fold
/// operations it allows to perform.
///
/// The input iterator must yield item in the form of `(K, V)` where the
/// value of type `K` will be used as key to identify the groups and the
/// value of type `V` as value for the folding operation.
///
/// See [`GroupingMap`] for more informations
/// on what operations are available.
#[cfg(feature = "use_std")]
fn into_grouping_map_with_hasher<K, V, S>(self, hash_builder: S) -> GroupingMap<Self, S>
where
Self: Iterator<Item = (K, V)> + Sized,
K: Hash + Eq,
S: BuildHasher,
{
grouping_map::with_hasher(self, hash_builder)
}

/// Constructs a `GroupingMap` to be used later with one of the efficient
/// group-and-fold operations it allows to perform.
///
Expand All @@ -3817,7 +3837,7 @@ pub trait Itertools: Iterator {
/// See [`GroupingMap`] for more informations
/// on what operations are available.
#[cfg(feature = "use_std")]
fn into_grouping_map_by<K, V, F>(self, key_mapper: F) -> GroupingMapBy<Self, F>
fn into_grouping_map_by<K, V, F>(self, key_mapper: F) -> GroupingMapBy<Self, F, RandomState>
where
Self: Iterator<Item = V> + Sized,
K: Hash + Eq,
Expand All @@ -3826,6 +3846,33 @@ pub trait Itertools: Iterator {
grouping_map::new(grouping_map::new_map_for_grouping(self, key_mapper))
}

/// Constructs a `GroupingMap`, which will use the given hash builder to
/// hash keys, to be used later with one of the efficient group-and-fold
/// operations it allows to perform.
///
/// The values from this iterator will be used as values for the folding operation
/// while the keys will be obtained from the values by calling `key_mapper`.
///
/// See [`GroupingMap`] for more informations
/// on what operations are available.
#[cfg(feature = "use_std")]
fn into_grouping_map_by_with_hasher<K, V, F, S>(
self,
key_mapper: F,
hash_builder: S,
) -> GroupingMapBy<Self, F, S>
where
Self: Iterator<Item = V> + Sized,
K: Hash + Eq,
F: FnMut(&V) -> K,
S: BuildHasher,
{
grouping_map::with_hasher(
grouping_map::new_map_for_grouping(self, key_mapper),
hash_builder,
)
}

/// Return all minimum elements of an iterator.
///
/// # Examples
Expand Down