lowdash 0.5.3

A Lodash inspired utility library to manipulate array and object for Rust
Documentation
use std::collections::HashMap;
use std::hash::Hash;

/// Creates a `HashMap` by transforming each element in a collection into a key-value pair using a provided function.
///
/// This function takes a slice of items and a transform function, then returns a new `HashMap<K, V>` where each key-value pair
/// is generated by applying the transform function to an element in the collection. If multiple elements produce the same key,
/// the last occurrence will overwrite previous ones.
///
/// **Time Complexity:**  
/// O(n), where n is the number of elements in the collection.
///
/// # Arguments
///
/// * `collection` - A slice of items to be transformed into key-value pairs.
/// * `transform` - A function that takes an item from the collection and returns a key-value pair.
///
/// # Type Parameters
///
/// * `T` - The type of elements in the collection.
/// * `K` - The type of keys in the resulting `HashMap`. Must implement `Eq` and `Hash`.
/// * `V` - The type of values in the resulting `HashMap`.
/// * `F` - The type of the transform function. Must implement `Fn(&T) -> (K, V)`.
///
/// # Returns
///
/// * `HashMap<K, V>` - A `HashMap` where each key-value pair is the result of applying the transform function to an element in the collection.
///
/// # Examples
///
/// ```rust
/// use lowdash::associate;
/// use std::collections::HashMap;
///
/// let numbers = vec![1, 2, 3, 4, 5];
/// let map = associate(&numbers, |&x| (x, x * x));
/// let mut expected = HashMap::new();
/// expected.insert(1, 1);
/// expected.insert(2, 4);
/// expected.insert(3, 9);
/// expected.insert(4, 16);
/// expected.insert(5, 25);
/// assert_eq!(map, expected);
/// ```
///
/// ```rust
/// use lowdash::associate;
/// use std::collections::HashMap;
///
/// #[derive(Debug, PartialEq, Clone)]
/// struct Person {
///     name: String,
///     age: u32,
/// }
///
/// let people = vec![
///     Person { name: "Alice".to_string(), age: 25 },
///     Person { name: "Bob".to_string(), age: 30 },
///     Person { name: "Charlie".to_string(), age: 35 },
/// ];
///
/// let map = associate(&people, |person| (person.name.clone(), person.age));
/// let mut expected = HashMap::new();
/// expected.insert("Alice".to_string(), 25);
/// expected.insert("Bob".to_string(), 30);
/// expected.insert("Charlie".to_string(), 35);
/// assert_eq!(map, expected);
/// ```
///
/// ```rust
/// use lowdash::associate;
/// use std::collections::HashMap;
///
/// let strings = vec!["apple", "banana", "apricot", "blueberry"];
/// let map = associate(&strings, |s| (s.chars().next().unwrap(), s.len()));
/// let mut expected = HashMap::new();
/// expected.insert('a', 7); // "apricot" has 7 characters
/// expected.insert('b', 9); // "blueberry" has 9 characters
/// assert_eq!(map, expected);
/// ```
pub fn associate<T, K, V, F>(collection: &[T], transform: F) -> HashMap<K, V>
where
    K: Eq + Hash,
    F: Fn(&T) -> (K, V),
{
    let mut result = HashMap::with_capacity(collection.len());

    for item in collection {
        let (key, value) = transform(item);
        result.insert(key, value);
    }

    result
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::collections::HashMap;

    #[derive(Debug, PartialEq, Clone)]
    struct Person {
        name: String,
        age: u32,
    }

    #[test]
    fn test_associate_integers() {
        let numbers = vec![1, 2, 3, 4, 5];
        let map = associate(&numbers, |&x| (x, x * x));
        let mut expected = HashMap::new();
        expected.insert(1, 1);
        expected.insert(2, 4);
        expected.insert(3, 9);
        expected.insert(4, 16);
        expected.insert(5, 25);
        assert_eq!(map, expected);
    }

    #[test]
    fn test_associate_strings() {
        let strings = vec!["apple", "banana", "apricot", "blueberry"];
        let map = associate(&strings, |s| (s.chars().next().unwrap(), s.len()));
        let mut expected = HashMap::new();
        expected.insert('a', 7); // "apricot" has 7 characters
        expected.insert('b', 9); // "blueberry" has 9 characters
        assert_eq!(map, expected);
    }

    #[test]
    fn test_associate_with_structs() {
        let people = vec![
            Person {
                name: "Alice".to_string(),
                age: 25,
            },
            Person {
                name: "Bob".to_string(),
                age: 30,
            },
            Person {
                name: "Charlie".to_string(),
                age: 35,
            },
        ];

        let map = associate(&people, |person| (person.name.clone(), person.age));
        let mut expected = HashMap::new();
        expected.insert("Alice".to_string(), 25);
        expected.insert("Bob".to_string(), 30);
        expected.insert("Charlie".to_string(), 35);
        assert_eq!(map, expected);
    }

    #[test]
    fn test_associate_with_duplicate_keys() {
        let numbers = vec![1, 2, 3, 2, 4, 3, 5];
        let map = associate(&numbers, |&x| (x % 2, x));
        let mut expected = HashMap::new();
        expected.insert(1, 5); // Last odd number
        expected.insert(0, 4); // Last even number
        assert_eq!(map, expected);
    }

    #[test]
    fn test_associate_with_empty_collection() {
        let empty: Vec<i32> = vec![];
        let map: HashMap<i32, i32> = associate(&empty, |&x| (x, x));
        let expected: HashMap<i32, i32> = HashMap::new();
        assert_eq!(map, expected);
    }

    #[test]
    fn test_associate_with_single_element() {
        let single = vec![42];
        let map = associate(&single, |&x| (x, x));
        let mut expected = HashMap::new();
        expected.insert(42, 42);
        assert_eq!(map, expected);
    }

    #[test]
    fn test_associate_preserves_latest_value() {
        let numbers = vec![10, 20, 30, 20, 10];
        let map = associate(&numbers, |&x| (x / 10, x));
        let mut expected = HashMap::new();
        expected.insert(1, 10); // Last occurrence
        expected.insert(2, 20); // Last occurrence
        expected.insert(3, 30);
        assert_eq!(map, expected);
    }

    #[test]
    fn test_associate_with_optionals() {
        let collection = vec![Some(1), None, Some(2), Some(1), None, Some(3), Some(2)];
        let map = associate(&collection, |&x| (x, x.map(|v| v * 2)));
        let mut expected = HashMap::new();
        expected.insert(Some(1), Some(2)); // Last occurrence
        expected.insert(None, None); // Last occurrence
        expected.insert(Some(2), Some(4)); // Last occurrence
        expected.insert(Some(3), Some(6));
        assert_eq!(map, expected);
    }

    #[test]
    fn test_associate_with_custom_key() {
        let people = vec![
            Person {
                name: "Alice".to_string(),
                age: 25,
            },
            Person {
                name: "Bob".to_string(),
                age: 30,
            },
            Person {
                name: "Charlie".to_string(),
                age: 35,
            },
            Person {
                name: "Alice".to_string(),
                age: 28,
            },
        ];

        let map = associate(&people, |person| (person.name.clone(), person.age));
        let mut expected = HashMap::new();
        expected.insert("Alice".to_string(), 28); // Last Alice
        expected.insert("Bob".to_string(), 30);
        expected.insert("Charlie".to_string(), 35);
        assert_eq!(map, expected);
    }

    #[test]
    fn test_associate_with_hashmap_keys() {
        let collection = vec![("a", 1), ("b", 2), ("a", 3), ("c", 4)];
        let map = associate(&collection, |&(k, v)| (k.to_string(), v * 2));
        let mut expected = HashMap::new();
        expected.insert("a".to_string(), 6);
        expected.insert("b".to_string(), 4);
        expected.insert("c".to_string(), 8);
        assert_eq!(map, expected);
    }

    #[test]
    fn test_associate_with_vectors_as_values() {
        let collection = vec![vec![1], vec![2, 2], vec![1], vec![3, 3, 3]];
        let map = associate(&collection, |v| (v.len(), v.clone()));
        let mut expected = HashMap::new();
        expected.insert(1, vec![1]); // Last vector with length 1
        expected.insert(2, vec![2, 2]); // Last vector with length 2
        expected.insert(3, vec![3, 3, 3]); // Last vector with length 3
        assert_eq!(map, expected);
    }
}