1
\$\begingroup\$

I have the following code which finds all the unique values from a hashmap of String to Vec. I cobbled it together (eventually as I was getting various borrow errors along the way), but it looks a mess to me and I'm looking for a more idiomatic way of doing it in rust.

fn main() {
    let mut my_map: HashMap<String, Vec<String>> = HashMap::new();
    my_map.insert("option1".to_string(), vec![String::from("sarah"), String::from("john")]);
    my_map.insert("option2".to_string(), vec![String::from("john"), String::from("mark")]);

    let x: Vec<String> = my_map.values().flat_map(|x| x).cloned().collect();
    // or use flatten() instead of flat_map(|x| x)
    let y: HashSet<&String> = HashSet::from_iter(x.iter());
    let mut z = y.iter().collect::<Vec<_>>();
    z.sort();
    println!("{:?}", z);
}

My idea was to flatmap the values from the map down into a simple vector, and then find the unique values (putting everything into a hashset), then converting back to a vector of strings.

The output is the sorted list of all unique names in the Vectors:

["john", "mark", "sarah"]

What would be a better implementation of this in rust?

\$\endgroup\$

1 Answer 1

3
\$\begingroup\$

You can collect into a different collection type, such as HashSet or BTreeSet. Both of these will eliminate duplicates, but entries in a HashSet will be in a random order (because the hash is randomized) while entries in a BTreeSet will be ordered (based on the Ord implementation for the element type).

use std::collections::{BTreeSet, HashMap};

fn main() {
    let mut my_map: HashMap<String, Vec<String>> = HashMap::new();
    my_map.insert("option1".to_string(), vec![String::from("sarah"), String::from("john")]);
    my_map.insert("option2".to_string(), vec![String::from("john"), String::from("mark")]);

    let b: BTreeSet<_> = my_map.values().flatten().collect();
    println!("{:?}", b);
}

Note that instead of annotating the variable type, you can use the "turbofish" to specify the collection type to collect into:

let b = my_map.values().flatten().collect::<BTreeSet<_>>();

itertools is a crate that provides lots of extension methods for iterators. Relevant here are methods such as unique and sorted_unstable.

use std::collections::HashMap;
use itertools::Itertools; // 0.10.3

fn main() {
    let mut my_map: HashMap<String, Vec<String>> = HashMap::new();
    my_map.insert("option1".to_string(), vec![String::from("sarah"), String::from("john")]);
    my_map.insert("option2".to_string(), vec![String::from("john"), String::from("mark")]);

    let x: Vec<_> = my_map.values().flatten().unique().sorted_unstable().collect();
    println!("{:?}", x);
}
\$\endgroup\$
1
  • \$\begingroup\$ Good answer. One clarification: turbofish isn't an operator. It's a different kind of type annotation. \$\endgroup\$ Jan 1, 2022 at 17:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.