79

In Scala, there is a method named toMap that works on any list of tuples and converts it to a map where the key is the first item on the tuple and the value is the second one:

val listOfTuples = List(("one", 1), ("two", 2))
val map = listOfTuples.toMap 

What is the closest thing to toMap in Rust?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Eran Medan
  • 44,555
  • 61
  • 184
  • 276
  • In short: `use std::collections::HashMap; let map: HashMap<&str, u32> = [("a", 5), ("b", 6), ("a", 7)].into_iter().collect(); println!("{:?}", map); // sometimes {"b": 6, "a": 7}, sometimes {"a": 6, "b": 7}` (note that the merging behaviour seems to be "use latest") – Leonard Pauli Dec 04 '21 at 14:01

3 Answers3

127

Use Iterator::collect:

use std::collections::HashMap;

fn main() {
    let tuples = [("one", 1), ("two", 2), ("three", 3)];
    let m: HashMap<_, _> = tuples.into_iter().collect();
    println!("{:?}", m);
}

collect leverages the FromIterator trait. Any iterator can be collected into a type that implements FromIterator. In this case, HashMap implements it as:

impl<K, V, S> FromIterator<(K, V)> for HashMap<K, V, S>
where
    K: Eq + Hash,
    S: HashState + Default,

Said another way, any iterator of tuples where the first value can be hashed and compared for total equality can be converted to a HashMap. The S parameter isn't exciting to talk about, it just defines what the hashing method is.

With this knowledge, you can also call FromIterator directly:

use std::collections::HashMap;

fn main() {
    let m: HashMap<_, _> = HashMap::from_iter([("one", 1), ("two", 2), ("three", 3)]);
    println!("{:?}", m);
}

See also:


what change should I make so that I get all the values with same key stored in a Vec?

There's no one-line / functional method for this in the standard library. Instead, use the entry API:

use std::collections::HashMap;

fn main() {
    let tuples = vec![("one", 1), ("two", 2), ("one", 3)];
    let mut m = HashMap::new();
    for (k, v) in tuples {
        m.entry(k).or_insert_with(Vec::new).push(v)
    }
    println!("{:?}", m);
}

If you found yourself doing this frequently, you could create your own type and implement FromIterator for it:

use std::{cmp::Eq, collections::HashMap, hash::Hash, iter::FromIterator};

struct MyCoolType<K: Eq + Hash, V>(HashMap<K, Vec<V>>);

impl<K: Eq + Hash, V> FromIterator<(K, V)> for MyCoolType<K, V> {
    fn from_iter<I>(tuples: I) -> Self
    where
        I: IntoIterator<Item = (K, V)>,
    {
        let mut m = HashMap::new();
        for (k, v) in tuples {
            m.entry(k).or_insert_with(Vec::new).push(v)
        }
        Self(m)
    }
}

fn main() {
    let tuples = vec![("one", 1), ("two", 2), ("one", 3)];
    let MyCoolType(m) = tuples.into_iter().collect();
    println!("{:?}", m);
}

See also:

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • Nice, so the `FromIterator` is Rust's `CanBuildFrom` :) (I know it's not but it's surely easier to understand...) – Eran Medan May 25 '15 at 16:03
  • What we have duplicate keys: `let tuples = vec![("one", 1), ("two", 2), ("one", 11)];`. How does the current solution behave? And what change should I make so that I get all the values with _same_ key stored in a `Vec`? – Nawaz Dec 12 '19 at 17:58
  • 1
    @Nawaz *How does the current solution behave* — does something prevent you from trying this yourself? The [playground](https://play.rust-lang.org/) is an excellent resource for that type of question. – Shepmaster Dec 12 '19 at 18:33
  • 1
    @Shepmaster: Yes, I should have tried that on the playground. Thanks. :-).. and thanks for the updating the solution as well. I came up with [almost same solution, using `fold` though instead of explicit `for` loop](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=d5a652229d6c44f8e68432f33cf4c7c0): – Nawaz Dec 12 '19 at 18:50
8
let map = HashMap::from([
  ("one", 1), 
  ("two", 2)
]);

Playground

  • While @Shepmaster's answer is more thorough and informative, Kamil's provides the most straightforward solution. Perhaps Shepmaster could update his answer to use it? – AmigoNico Jul 21 '22 at 16:53
2

Since it wasn't already mentioned, here is a single line (albeit long) method:

use std::collections::HashMap;

fn main() {
   let m: HashMap<&str, u16> = [("year", 2019), ("month", 12)].iter().cloned().collect();
   println!("{:?}", m);
}

Or you can do a Trait:

use std::collections::HashMap;

trait Hash {
   fn to_map(&self) -> HashMap<&str, u16>;
}

impl Hash for [(&str, u16)] {
   fn to_map(&self) -> HashMap<&str, u16> {
      self.iter().cloned().collect()
   }
}

fn main() {
   let m = [("year", 2019), ("month", 12)].to_map();
   println!("{:?}", m)
}

https://doc.rust-lang.org/std/collections/struct.HashMap.html#examples

Zoe
  • 27,060
  • 21
  • 118
  • 148
Zombo
  • 1
  • 62
  • 391
  • 407