# Hash Map
Hash maps are defined as `HashMap<K, V>`, where it stores a mapping of keys of type `K` to values of type `V` using a hashing function.

This determines how it places these keys and values into memory

Other programming support this kind of data structure its often called a different name, such as *hash*, *map*, *object*, *hash table*, *dictionary*, or *associative array*, these are just some of the names

Hash maps are useful when you want to look up data not by using an index, as you can with vectors, but instead you use a key that can be any type

For example it could be different teams scores where the name of the team is the key and the score is the value

Check the std library documentation for more info

## Creating a New Hash Map
One way to create an empty hash map is to use `new` and to add elements with `insert`

Lets say we are keeping track of the score of two teams whose names are *Blue* and *Yellow*

Blue team has 10 points
Yellow team has 50 points

```rust
    use std::collections::HashMap;

    let mut scores = HashMap::new();

    scores.insert(String::from("Blue"), 10);
    scores.insert(String::from("Yellow"), 50);
```

Note we must bring HashMap into scope because it is not included by default

Hash Maps also has less support from the std library, therefore there is no macros to construct them and there are less methods associated with them

They are store on the heap
The keys must be all be the same type as each other
The values must be all be the same type as each other

Keys and Values do not need to be the same type as each other

## Accessing Values in a Hash Map

We can get a value out of the hash map by using its key to the `get` method

Here is an example
```rust
    use std::collections::HashMap;

    let mut scores = HashMap::new();

    scores.insert(String::from("Blue"), 10);
    scores.insert(String::from("Yellow"), 50);

    let team_name = String::from("Blue");
    let score = scores.get(&team_name).copied().unwrap_or(0);
```

This would result in score being equal to 10

The `get` method returns an `Option<&V>`, if there is no value for that key in the hash map, then the Option will be `None`

This program uses `copied` to get an `Option<i32>` rather than a `Option<&i32>` then `unwrap_or` to set score to zero if there is no entry for the key

You can iterate over each key-value pair in the hash map in a similar way as vectors using a `for` loop

```rust
    use std::collections::HashMap;

    let mut scores = HashMap::new();

    scores.insert(String::from("Blue"), 10);
    scores.insert(String::from("Yellow"), 50);

    for (key, value) in &scores {
        println!("{key}: {value}");
    }
```


This would output each pair in an arbitrary order
```
Yellow: 50
Blue: 10
```

## Hash Maps and Ownership

If a type implements the `Copy` trait values are copied into the hash map
For owned values like `String` the values with be moved and the hash map will be the owner of those values

Example
```rust
    use std::collections::HashMap;

    let field_name = String::from("Favorite color");
    let field_value = String::from("Blue");

    let mut map = HashMap::new();
    map.insert(field_name, field_value);
    // field_name and field_value are invalid at this point, try using them and
    // see what compiler error you get!
```

You aren't able to use `field_name` nor `field_value` because ownership of the associated value from those two variables has changed

This change happened after the `insert` method was called

If references are inserting into the hash map, then the values wont be moved but the reference MUST be valid for as long as the hash map is also valid

## Updating a Hash Map

While a Hash Map is growable each key MUST by unique and can only be associated with a single value at a time.
The reverse is not true

When you want to change the data in a hash map, you have to decided how to handle the case when a key already has a value assigned

### Overwriting a Value

If you insert a key and value into a hash map then insert the same key with a different value then the value associated with the key will be overwritten
```rust
    use std::collections::HashMap;

    let mut scores = HashMap::new();

    scores.insert(String::from("Blue"), 10);
    scores.insert(String::from("Blue"), 25);

    println!("{scores:?}");
```

This only contains 1 key: value
This will output `{"Blue": 25}`
The original 10 is overwritten

### Adding a Key and Value Only If a Key Isn't Present
This is common do do if there is no key associated with a desired value, then add the key and value otherwise do nothing.

Hash Maps have a special API for this `entry` that takes the key you want to check as a parameter

The return value of the `entry` method is an enum called Entry that represents a value that might or might not exist

This can be used if it doesn't have a value associated with the key then insert the value

Example of use
```rust
    use std::collections::HashMap;

    let mut scores = HashMap::new();
    scores.insert(String::from("Blue"), 10);

    scores.entry(String::from("Yellow")).or_insert(50);
    scores.entry(String::from("Blue")).or_insert(50);

    println!("{scores:?}");
```

The `or_insert` method on `Entry` is defined to return a mutable reference to the value for the corresponding `Entry` key if that key exists and if not it inserts the parameter as the new value for this key and returns a mutable refer to the new value

This is more clear then writing our own definition that complies with the borrow checker

This code will output `{"Yellow": 50, "Blue": 10}`

This means that this `or_insert` will not change it if it already exists

### Updating a Value Based on the Old Value
Another common use case for hash maps is to look up a key's value then update it based on the old value

Example
```rust
    use std::collections::HashMap;

    let text = "hello world wonderful world";

    let mut map = HashMap::new();

    for word in text.split_whitespace() {
        let count = map.entry(word).or_insert(0);
        *count += 1;
    }

    println!("{map:?}");
```

This counts how many times each word appears in some text

If its the first time we see that word we initialize it to zero, then we add one using a reference to that key's value

This will output `{"world": 2, "hello": 1, "wonderful": 1}`

Output order is arbitrary when iterating over a hash map

`split_whitespace` method returns an iterator over sub slices, separated by whitespace, of the value in `text`.

`or_insert` method returns a mutable reference (`&mut V`) to the value for the specified key

In this case this reference is stored in `count` variable, then it is dereferenced `count` using `*` to assign a value to it. The reference then goes out of scope which ensures that the reference is freed and allows for the new assignment of another mutable reference to `count`. This makes all of the changes by the mutable reference safe because of the use of a `for` loop.

## Hashing Functions

By default `HashMap` uses a hashing function called [*SipHash*](https://en.wikipedia.org/wiki/SipHash) that can provided resistance to DoS (denial-of-service) attacks involving hash tables

This is not the fastest hashing algorithm available, but it the trade-off is for better security but with a drop in performance. This is worth it.

If your use case requires a different or faster hash you can switch by specifying a different hasher

A *hasher* is a type that implements the `BuildHasher` trait

Many other types of hashers can be provided in library crates which can be found on [crates.io](https://crates.io) which implement many common hashing algorithms.