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?
1 Answer 1
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);
}
-
\$\begingroup\$ Good answer. One clarification: turbofish isn't an operator. It's a different kind of type annotation. \$\endgroup\$Caleb Stanford– Caleb Stanford2022年01月01日 17:10:29 +00:00Commented Jan 1, 2022 at 17:10