Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I create a map from a list in a functional way?

Tags:

rust

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?

like image 549
Eran Medan Avatar asked May 25 '15 15:05

Eran Medan


2 Answers

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:

  • Collect iterators of length 2 into HashMap
  • How do I create a HashMap literal?

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:

  • How to lookup from and insert into a HashMap efficiently?
like image 63
Shepmaster Avatar answered Sep 20 '22 05:09

Shepmaster


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

like image 30
Zombo Avatar answered Sep 21 '22 05:09

Zombo