In a recent project I had a HashMap and I needed to create another HashMap using the same keys, and calculating the value based on the values from the original hash. More precisely I had to calculate the percentage of each value from the total and convert it into a string to be displayed later.
In the following example we'll see two approaches for this and we'll see the second approach twice to make it clearer.
In the first case I've creates an empty mutable HashMap, iterated over the keys and inserted the new key-value pairs.
let mut perc: HashMap<&str, String> = HashMap::new();
for field in stats.keys() {
perc.insert(field, percentage(stats[field], total));
}
In the second example I took a more "functional" approach.
let perc: HashMap<&&str, String> = HashMap::from_iter(
stats
.iter()
.map(|(k, v)| (k, percentage(*v, total)))
.collect::<Vec<(&&str, String)>>(),
);
As it felt a bit too complex I also created a two-step version of this approach.
First we create a vector of tuples that will be the future key-value pairs of the
new HashMap and then we build the hash using the from_iter
call.
let pairs = stats
.iter()
.map(|(k, v)| (k, percentage(*v, total)))
.collect::<Vec<(&&str, String)>>();
let perc: HashMap<&&str, String> = HashMap::from_iter(pairs);
The full code
examples/convert-hash-to-hash/src/main.rs
use std::collections::HashMap;
fn main() {
with_mutable();
println!("---------------");
functional();
println!("---------------");
functional_temp();
}
fn with_mutable() {
let stats: HashMap<&str, usize> = HashMap::from([("red", 10), ("green", 39), ("blue", 23)]);
let total = stats.values().sum::<usize>();
let mut perc: HashMap<&str, String> = HashMap::new();
for field in stats.keys() {
perc.insert(field, percentage(stats[field], total));
}
dbg!(&stats);
dbg!(total);
dbg!(perc);
}
fn functional() {
let stats: HashMap<&str, usize> = HashMap::from([("red", 10), ("green", 39), ("blue", 23)]);
let total = stats.values().sum::<usize>();
let perc: HashMap<&&str, String> = HashMap::from_iter(
stats
.iter()
.map(|(k, v)| (k, percentage(*v, total)))
.collect::<Vec<(&&str, String)>>(),
);
dbg!(&stats);
dbg!(total);
dbg!(perc);
}
fn functional_temp() {
let stats: HashMap<&str, usize> = HashMap::from([("red", 10), ("green", 39), ("blue", 23)]);
let total = stats.values().sum::<usize>();
let pairs = stats
.iter()
.map(|(k, v)| (k, percentage(*v, total)))
.collect::<Vec<(&&str, String)>>();
let perc: HashMap<&&str, String> = HashMap::from_iter(pairs);
dbg!(&stats);
dbg!(total);
dbg!(perc);
}
pub fn percentage(num: usize, total: usize) -> String {
let total = (10000.0 * num as f32 / total as f32).floor();
(total / 100.0).to_string()
}