use std::collections::HashMap;

pub fn merge<K, V>(map1: HashMap<K, V>, map2: HashMap<K, V>) -> HashMap<K, V>
where
    K: std::hash::Hash + Eq + Clone,
    V: Clone,
{
    let mut result = map1;
    result.extend(map2);
    result
}

pub fn filter_by_key<K, V, F>(map: HashMap<K, V>, predicate: F) -> HashMap<K, V>
where
    K: std::hash::Hash + Eq + Clone,
    V: Clone,
    F: Fn(&K) -> bool,
{
    map.into_iter()
        .filter(|(k, _)| predicate(k))
        .collect()
}

pub fn filter_by_value<K, V, F>(map: HashMap<K, V>, predicate: F) -> HashMap<K, V>
where
    K: std::hash::Hash + Eq + Clone,
    V: Clone,
    F: Fn(&V) -> bool,
{
    map.into_iter()
        .filter(|(_, v)| predicate(v))
        .collect()
}

pub fn map_values<K, V, U, F>(map: HashMap<K, V>, f: F) -> HashMap<K, U>
where
    K: std::hash::Hash + Eq + Clone,
    F: Fn(V) -> U,
{
    map.into_iter().map(|(k, v)| (k, f(v))).collect()
}

pub fn invert<K, V>(map: HashMap<K, V>) -> HashMap<V, K>
where
    K: std::hash::Hash + Eq + Clone,
    V: std::hash::Hash + Eq + Clone,
{
    map.into_iter().map(|(k, v)| (v, k)).collect()
}