Skip to content

Instantly share code, notes, and snippets.

@starfys
Last active May 22, 2020 22:44
Show Gist options
  • Save starfys/c0da3469f2514652d85b78cd27bce370 to your computer and use it in GitHub Desktop.
Save starfys/c0da3469f2514652d85b78cd27bce370 to your computer and use it in GitHub Desktop.
Comparing hash maps (fully generic)
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