diff --git a/src/group_map.rs b/src/group_map.rs index 3dcee83af..81c94b23b 100644 --- a/src/group_map.rs +++ b/src/group_map.rs @@ -1,5 +1,6 @@ #![cfg(feature = "use_std")] +use core::hash::BuildHasher; use std::collections::HashMap; use std::hash::Hash; use std::iter::Iterator; @@ -8,12 +9,13 @@ use std::iter::Iterator; /// /// See [`.into_group_map()`](crate::Itertools::into_group_map) /// for more information. -pub fn into_group_map(iter: I) -> HashMap> +pub fn into_group_map_with_hasher(iter: I, hash_builder: S) -> HashMap, S> where I: Iterator, K: Hash + Eq, + S: BuildHasher, { - let mut lookup = HashMap::new(); + let mut lookup = HashMap::with_hasher(hash_builder); iter.for_each(|(key, val)| { lookup.entry(key).or_insert_with(Vec::new).push(val); @@ -22,11 +24,16 @@ where lookup } -pub fn into_group_map_by(iter: I, mut f: F) -> HashMap> +pub fn into_group_map_by_with_hasher( + iter: I, + mut f: F, + hash_builder: S, +) -> HashMap, S> where I: Iterator, K: Hash + Eq, F: FnMut(&V) -> K, + S: BuildHasher, { - into_group_map(iter.map(|v| (f(&v), v))) + into_group_map_with_hasher(iter.map(|v| (f(&v), v)), hash_builder) } diff --git a/src/lib.rs b/src/lib.rs index d83892159..732fbf25f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3792,7 +3792,36 @@ pub trait Itertools: Iterator { Self: Iterator + Sized, K: Hash + Eq, { - group_map::into_group_map(self) + group_map::into_group_map_with_hasher(self, RandomState::new()) + } + + /// Return a `HashMap` of keys mapped to `Vec`s of values, using the hash builder for hashing. + /// See [.into_group_map()](crate::Itertools::into_group_map) for more information. + /// + /// Warning: `hash_builder` is normally randomly generated, and is designed to allow it's + /// users to be resistant to attacks that cause many collisions and very poor performance. + /// Setting it manually using this function can expose a DoS attack vector. + /// + /// ``` + /// use std::hash::RandomState; + /// use itertools::Itertools; + /// + /// let data = vec![(0, 10), (2, 12), (3, 13), (0, 20), (3, 33), (2, 42)]; + /// let lookup = data.into_iter().into_group_map_with_hasher(RandomState::new()); + /// + /// assert_eq!(lookup[&0], vec![10, 20]); + /// assert_eq!(lookup.get(&1), None); + /// assert_eq!(lookup[&2], vec![12, 42]); + /// assert_eq!(lookup[&3], vec![13, 33]); + /// ``` + #[cfg(feature = "use_std")] + fn into_group_map_with_hasher(self, hash_builder: S) -> HashMap, S> + where + Self: Iterator + Sized, + K: Hash + Eq, + S: BuildHasher, + { + group_map::into_group_map_with_hasher(self, hash_builder) } /// Return a `HashMap` of keys mapped to `Vec`s of values. The key is specified @@ -3829,7 +3858,52 @@ pub trait Itertools: Iterator { K: Hash + Eq, F: FnMut(&V) -> K, { - group_map::into_group_map_by(self, f) + group_map::into_group_map_by_with_hasher(self, f, RandomState::new()) + } + + /// Return a `HashMap` of keys mapped to `Vec`s of values, using the hash builder for hashing. + /// See [.into_group_map_by()](crate::Itertools::into_group_map_by) for more information. + /// + /// Warning: `hash_builder` is normally randomly generated, and is designed to allow it's + /// users to be resistant to attacks that cause many collisions and very poor performance. + /// Setting it manually using this function can expose a DoS attack vector. + /// + /// ``` + /// use itertools::Itertools; + /// use std::collections::HashMap; + /// use std::hash::RandomState; + /// + /// let data = vec![(0, 10), (2, 12), (3, 13), (0, 20), (3, 33), (2, 42)]; + /// let lookup: HashMap> = + /// data.clone().into_iter().into_group_map_by_with_hasher(|a| a.0, RandomState::new()); + /// + /// assert_eq!(lookup[&0], vec![(0,10), (0,20)]); + /// assert_eq!(lookup.get(&1), None); + /// assert_eq!(lookup[&2], vec![(2,12), (2,42)]); + /// assert_eq!(lookup[&3], vec![(3,13), (3,33)]); + /// + /// assert_eq!( + /// data.into_iter() + /// .into_group_map_by_with_hasher(|x| x.0, RandomState::new()) + /// .into_iter() + /// .map(|(key, values)| (key, values.into_iter().fold(0,|acc, (_,v)| acc + v ))) + /// .collect::>()[&0], + /// 30, + /// ); + /// ``` + #[cfg(feature = "use_std")] + fn into_group_map_by_with_hasher( + self, + f: F, + hash_builder: S, + ) -> HashMap, S> + where + Self: Iterator + Sized, + K: Hash + Eq, + F: FnMut(&V) -> K, + S: BuildHasher, + { + group_map::into_group_map_by_with_hasher(self, f, hash_builder) } /// Constructs a `GroupingMap` to be used later with one of the efficient