diff --git a/unreal-sdk/src/lib.rs b/unreal-sdk/src/lib.rs index 4f487a5..6ab72b2 100644 --- a/unreal-sdk/src/lib.rs +++ b/unreal-sdk/src/lib.rs @@ -16,3 +16,4 @@ pub mod v2_types; pub mod any_type; pub mod types; +pub mod util; diff --git a/unreal-sdk/src/util.rs b/unreal-sdk/src/util.rs new file mode 100644 index 0000000..fccff4c --- /dev/null +++ b/unreal-sdk/src/util.rs @@ -0,0 +1,210 @@ +use std::collections::hash_map::Entry; +use std::collections::HashMap; +use std::hash::Hash; + +pub struct DedupIdentity; + +pub trait DedupGetHash { + type Hashed: Hash + Eq; + fn get_hash<'a>(&mut self, a: &'a T) -> Self::Hashed; +} + +impl DedupGetHash for DedupIdentity +where + T: Hash + Eq + Clone, +{ + type Hashed = T; + + fn get_hash<'a>(&mut self, a: &'a T) -> Self::Hashed { + a.clone() + } +} + +impl DedupGetHash for F +where + F: FnMut(&T) -> H, + H: Hash + Eq, +{ + type Hashed = H; + + fn get_hash<'a>(&mut self, a: &'a T) -> Self::Hashed { + self(a) + } +} + +pub struct DedupWithBy +where + I: Iterator, + F: FnMut(usize, ::Item) -> ::Item, + H: DedupGetHash<::Item>, +{ + iter: I, + /// makes item unique, is told how many elements of this equality. + cb_fn: F, + /// returns the borrowed element by which to compare the iterators elements + hash_fn: H, + cache: HashMap, +} + +pub trait DedupIter: Iterator { + fn dedup_with<'a, F>(self, f: F) -> DedupWithBy + where + F: FnMut(usize, ::Item) -> ::Item, + Self: Sized, + ::Item: Eq + Hash + Clone, + { + DedupWithBy { + iter: self, + cb_fn: f, + hash_fn: DedupIdentity, + cache: HashMap::new(), + } + } + + fn dedup_with_by<'a, F, H>(self, f: F, h: H) -> DedupWithBy + where + F: FnMut(usize, ::Item) -> ::Item, + H: DedupGetHash<::Item>, + Self: Sized, + { + DedupWithBy { + iter: self, + cb_fn: f, + hash_fn: h, + cache: HashMap::new(), + } + } +} + +impl DedupIter for I where I: Iterator {} + +impl Iterator for DedupWithBy +where + I: Iterator, + F: FnMut(usize, ::Item) -> ::Item, + H: DedupGetHash<::Item>, +{ + type Item = ::Item; + + fn next(&mut self) -> Option { + let mut next = self.iter.next()?; + + let next = loop { + let hash = self.hash_fn.get_hash(&next); + + match self.cache.entry(hash) { + Entry::Occupied(mut entry) => { + *entry.get_mut() += 1; + + next = (self.cb_fn)(*entry.get(), next); + } + Entry::Vacant(entry) => { + entry.insert(0); + break next; + } + }; + }; + + Some(next) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn numbers() { + let numbers = [1, 2, 2, 2, 3, 4, 5, 6, 22]; + + let unique = numbers + .into_iter() + .dedup_with(|n, i| (i * 10 + n) as usize) + .collect::>(); + + assert_eq!(&unique, &[1, 2, 21, 22, 3, 4, 5, 6, 221]); + } + + #[derive(Debug, PartialEq, Clone)] + struct Key { + number: usize, + other: f32, + } + + #[test] + fn keys() { + let keys = [ + Key { + number: 1, + other: 1.0, + }, + Key { + number: 2, + other: 1.0, + }, + Key { + number: 2, + other: 1.0, + }, + Key { + number: 2, + other: 1.0, + }, + Key { + number: 3, + other: 1.0, + }, + Key { + number: 4, + other: 1.0, + }, + Key { + number: 5, + other: 1.0, + }, + ]; + let expected = [ + Key { + number: 1, + other: 1.0, + }, + Key { + number: 2, + other: 1.0, + }, + Key { + number: 21, + other: 1.0, + }, + Key { + number: 22, + other: 1.0, + }, + Key { + number: 3, + other: 1.0, + }, + Key { + number: 4, + other: 1.0, + }, + Key { + number: 5, + other: 1.0, + }, + ]; + + let unique = keys + .into_iter() + .dedup_with_by( + |n, key| Key { + number: (key.number * 10 + n), + ..key + }, + |key: &Key| -> usize { key.number }, + ) + .collect::>(); + + assert_eq!(&unique, &expected); + } +}