Last active
May 22, 2020 22:44
-
-
Save starfys/c0da3469f2514652d85b78cd27bce370 to your computer and use it in GitHub Desktop.
Comparing hash maps (fully generic)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use std::collections::hash_map::{self, HashMap}; | |
use std::hash::Hash; | |
pub struct HashWrap<K, V>(HashMap<K, V>); | |
impl<K, V> From<HashMap<K, V>> for HashWrap<K, V> { | |
fn from(value: HashMap<K, V>) -> Self { | |
Self(value) | |
} | |
} | |
impl<K, V> HashWrap<K, V> { | |
/// Returns an iterator over all keys that exist in `self`, but not `other` | |
pub fn new_keys<'a, 'b>( | |
&'a self, | |
other: &'b HashMap<K, V>, | |
) -> HashMapDifference<'a, 'b, K, V, impl Fn(&'_ V, Option<&'_ V>) -> bool> { | |
HashMapDifference { | |
base: self.0.iter(), | |
compare: &other, | |
// Evaluates to true if the key does not exist in `other` | |
filter: |_: &V, compare: Option<&V>| compare.is_none(), | |
} | |
} | |
/// Returns an iterator over all keys that exist in both `self` and `other` but have different values | |
pub fn modified_values<'a, 'b>( | |
&'a self, | |
other: &'b HashMap<K, V>, | |
) -> HashMapDifference<'a, 'b, K, V, impl Fn(&'_ V, Option<&'_ V>) -> bool> | |
where | |
V: PartialEq, | |
{ | |
HashMapDifference { | |
base: self.0.iter(), | |
compare: &other, | |
// Evaluates to true if the key exists in `other` but the value different | |
filter: |base: &V, compare: Option<&V>| match compare { | |
Some(compare) => base != compare, | |
None => false, | |
}, | |
} | |
} | |
/// Returns an iterator over all keys that either | |
/// | |
/// * exist in self do not exist in `other` | |
/// * exist in `other` but have different values | |
pub fn new_keys_or_modified_values<'a, 'b>( | |
&'a self, | |
other: &'b HashMap<K, V>, | |
) -> HashMapDifference<'a, 'b, K, V, impl Fn(&'_ V, Option<&'_ V>) -> bool> | |
where | |
V: PartialEq, | |
{ | |
HashMapDifference { | |
base: self.0.iter(), | |
compare: &other, | |
// Evaluates to true if the key exists but the value is different | |
// in `other` or if the key doesn't exist in `other` | |
filter: |base: &V, compare: Option<&V>| match compare { | |
Some(compare) => base != compare, | |
None => true, | |
}, | |
} | |
} | |
/// Returns an iterator over all keys that do not exist in `self` but exist in `other` | |
pub fn missing_keys<'a, 'b>( | |
&'a self, | |
other: &'b HashMap<K, V>, | |
) -> HashMapDifference<'b, 'a, K, V, impl Fn(&'_ V, Option<&'_ V>) -> bool> { | |
// Here we swap self and other | |
// We want to check whether items in `other` exist in self | |
HashMapDifference { | |
base: other.iter(), | |
compare: &self.0, | |
// Evaluates to true if the key does not exist in `self` | |
filter: |_: &V, compare: Option<&V>| compare.is_none(), | |
} | |
} | |
} | |
pub struct HashMapDifference<'a, 'b, K, V, F: Fn(&'_ V, Option<&'_ V>) -> bool> { | |
base: hash_map::Iter<'a, K, V>, | |
compare: &'b HashMap<K, V>, | |
filter: F, | |
} | |
impl<'a, 'b, K, V, F> Iterator for HashMapDifference<'a, 'b, K, V, F> | |
where | |
K: Eq + Hash, | |
F: Fn(&'_ V, Option<&'_ V>) -> bool, | |
{ | |
type Item = &'a K; | |
fn next(&mut self) -> Option<Self::Item> { | |
while let Some((key, base_metadata)) = self.base.next() { | |
let compare_metadata = self.compare.get(key); | |
if (self.filter)(base_metadata, compare_metadata) { | |
return Some(key); | |
} | |
} | |
None | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment