Sort a hashmap by values in rust - rust

In python its done this way:
>>> x = {1: 2, 3: 4, 4: 3, 2: 1, 0: 0}
>>> {k: v for k, v in sorted(x.items(), key=lambda item: item[1])}
{0: 0, 2: 1, 1: 2, 4: 3, 3: 4}
How to sort a HashMap by values in rust?
My code so far:
use std::collections::HashMap;
fn main() {
let mut count: HashMap<String, u32>= HashMap::new();
count.insert(String::from("A"), 5);
count.insert(String::from("B"), 2);
count.insert(String::from("C"), 11);
count.insert(String::from("D"), 10);
let highest = count.iter().max_by(|a, b| a.1.cmp(&b.1)).unwrap();
println!("largest hash: {:?}", highest); // largest hash: ("C", 11)
}

Unlike Python's dict, Rust's "built-in" hashmap is not ordered, so sorting it has no effect.
If you need an ordered map for some reason, you should use indexmap. Alternatively, BTreeMap is sorted based on the key.
As you don't really present any sort of compelling use case it's hard to provide counsel though.

Ya, sorted it by converting to vector:
use std::collections::HashMap;
fn main() {
let mut count: HashMap<String, u32>= HashMap::new();
count.insert(String::from("A"), 5);
count.insert(String::from("B"), 2);
count.insert(String::from("C"), 11);
count.insert(String::from("D"), 10);
let mut hash_vec: Vec<(&String, &u32)> = count.iter().collect();
println!("{:?}", hash_vec);
hash_vec.sort_by(|a, b| b.1.cmp(a.1));
println!("Sorted: {:?}", hash_vec); //Sorted: [("C", 11), ("D", 10), ("A", 5), ("B", 2)]
}
Sort HashMap data by value

Related

How to convert BTreeSet to BTreeMap?

I have a BTreeSet object and I want to convert it into a BTreeMap with default indices (i.e., starting from 0 and incrementing). More precisely, given this:
let set = BTreeSet::from([0, 2, 4, 6, 8]);
I want to end up with BTreeMap<usize, usize> that holds [(0, 0), (1, 2), (2, 4), (3, 6), (4, 8)]. I can do it with a loop for example as
let mut tree = BTreeMap::new();
for (i, v) in set.clone().into_iter().enumerate() {
tree.insert(i, v);
}
But is there any shortcut (i.e., one line code) that does the same?
But is there any shortcut (i.e., one line code) that does the same?
let map = set.iter().copied().enumerate().collect::<BTreeMap<_, _>>();
println!("{:?}", map);
{0: 0, 1: 2, 2: 4, 3: 6, 4: 8}
tho note that this is a BTreeMap<usize, i32>, because i32 is the default resolution for {integer} literals if they're otherwise unconstrained, which is the case here.

Group characters by number of occurrences in a string with Itertools

I have two approaches to grouping characters by the number of occurrences in a string. One of them is using std::collections::HashMap and the second one is using itertools::Itertools::group_by. Unfortunately, grouping with Itertools gives me undesirable results.
Example input word: "Barbara"
Using std::collections::HashMap
let map1 = word.to_lowercase()
.chars()
.fold(HashMap::new(), |mut acc, c| {
*acc.entry(c).or_insert(0) += 1;
acc
});
Result {'a': 3, 'b': 2, 'r': 2}
And using itertools::Itertools::group_by
let map2: HashMap<char, u32> = word.to_lowercase()
.chars()
.group_by(|&x| x)
.into_iter()
.map(|(k, v)| (k, v.count() as u32))
.collect();
Result {'r': 1, 'a': 1, 'b': 1}
Oddly enough, when the input string has identical characters in succession, Itertools takes those characters into account.
The question is, what makes it return different results?
Playground
You're looking for into_group_map_by. group_by only groups consecutive elements according to the docs.
use itertools::Itertools;
use std::collections::HashMap;
fn main() {
let word = "Barbara";
let map1 = word
.to_lowercase()
.chars()
.fold(HashMap::new(), |mut acc, c| {
*acc.entry(c).or_insert(0) += 1;
acc
});
println!("{:?}", map1);
let map2: HashMap<char, u32> = word
.to_lowercase()
.chars()
.into_group_map_by(|&x| x)
.into_iter()
.map(|(k, v)| (k, v.len() as u32))
.collect();
println!("{:?}", map2);
}
Output:
{'b': 2, 'a': 3, 'r': 2}
{'b': 2, 'r': 2, 'a': 3}
Playground
There's also into_grouping_map_by, which can be used for this like:
let map2: HashMap<char, u32> = word
.to_lowercase()
.chars()
.into_grouping_map_by(|&x| x)
.fold(0, |acc, _key, _value| acc + 1);
The documentation says (emphasis added):
fn group_by<K, F>(self, key: F) -> GroupBy<K, Self, F>
where
Self: Sized,
F: FnMut(&Self::Item) -> K,
K: PartialEq,
Return an iterable that can group iterator elements. Consecutive elements that map to the same key (“runs”), are assigned to the same group.
It only groups consecutive elements. You'll need to sort the characters before calling group_by.
let map2: HashMap<char, u32> = word.to_lowercase()
.chars()
.sorted()
.group_by(|&x| x)
...
Output:
{'a': 3, 'r': 2, 'b': 2}
{'b': 2, 'a': 3, 'r': 2}
Playground

Single cyclic iteration from middle of rust vector

I have a vector of some elements that I would like to iterate from some index and cycle around from the front again while only visiting each element a single time.
Ex. starting here at index 2,
[0, 1, 2, 3, 4, 5, 6]
^
I would like to have an iterator over the elements [2, 3, 4, 5, 6, 0, 1] (and avoid writing a loop everywhere I need to run through the vector this way).
The standard iteration with cycle() + skip() seemed to be a good start, but it of cause never ends.
Is there any idiomatic way with rusts standard iterators?
You can iterate over the two subslices, and use chain to concatenate them together into a single iterator:
let v = vec![0, 1, 2, 3, 4, 5, 6];
let start_index = 2;
for e in v[start_index..].iter().chain(v[..start_index].iter()) {
println!("{}", e);
}
The obvious fix for your cycle/skip combo is to add a take() to limit it:
fn cycle<T>(slice: &[T], start_pos: usize) -> impl Iterator<Item = &T> {
slice.iter().cycle().skip(start_pos).take(slice.len())
}
Another option is to just chain the two ranges, which even ends up a bit shorter:
fn cycle<T>(slice: &[T], start_pos: usize) -> impl Iterator<Item = &T> {
slice[start_pos..].iter().chain(&slice[..start_pos])
}
Both versions pass test such as:
let v = vec![0, 1, 2, 3, 4, 5, 6];
assert_eq!(cycle(&v, 2).copied().collect::<Vec<_>>(), vec![2, 3, 4, 5, 6, 0, 1]);

Sorting a vector based on frequency and position of the elements in Rust

I have a vector and I want to sort it, where the first criterion is frequency. Second criterion is position in the vector. If two elements have the same number of occurrences, I want the most recently seen element to take advantage and go first. In the end, I want to remove duplicate elements from it.
For instance, if the input is this:
fn main() {
let history = vec![3, 2, 4, 6, 2, 4, 3, 3, 4, 5, 6, 3, 2, 4, 5, 5, 3];
}
The output should be:
3 4 5 2 6
How can I do this in Rust?
A straightforward method is to build hash maps for frequencies and positions of the elements:
use std::collections::HashMap;
fn frequency_map(nums: &[i32]) -> HashMap<i32, usize> {
let mut map = HashMap::new();
for &n in nums {
*map.entry(n).or_insert(0) += 1;
}
map
}
fn position_map(nums: &[i32]) -> HashMap<i32, usize> {
let mut map = HashMap::new();
for (pos, &n) in nums.iter().enumerate() {
map.insert(n, pos);
}
map
}
And then do an unstable sort by position followed by a stable sort by frequency:
fn custom_sort(nums: &mut Vec<i32>) {
let freq_map = frequency_map(nums);
let pos_map = position_map(nums);
nums.sort_unstable_by(|a, b| pos_map.get(b).unwrap().cmp(pos_map.get(a).unwrap()));
nums.dedup();
nums.sort_by(|a, b| freq_map.get(b).unwrap().cmp(freq_map.get(a).unwrap()));
}
Example:
use itertools::Itertools;
fn main() {
let mut history = vec![3, 2, 4, 6, 2, 4, 3, 3, 4, 5, 6, 3, 2, 4, 5, 5, 3];
custom_sort(&mut history);
println!("[{}]", history.iter().format(", "));
}
Output:
[3, 4, 5, 2, 6]
(playground)

Get indexes of all occurrences of a substring within a string

If I wanted to get the index of the first occurrence of, say, substring "foo" within a string "foo bar foo baz foo", I'd use:
fn main() {
let my_string = String::from("foo bar foo baz foo");
println!("{:?}", my_string.find("foo"));
}
...which would give me Some(0).
However, I need to find indexes of all occurrences of a substring within a string.
In this scenario, I'd need something like:
[0, 8, 16]
How can I do this idiomatically in Rust?
Use match_indices. Example from Rust docs:
let v: Vec<_> = "abcXXXabcYYYabc".match_indices("abc").collect();
assert_eq!(v, [(0, "abc"), (6, "abc"), (12, "abc")]);
let v: Vec<_> = "1abcabc2".match_indices("abc").collect();
assert_eq!(v, [(1, "abc"), (4, "abc")]);
let v: Vec<_> = "ababa".match_indices("aba").collect();
assert_eq!(v, [(0, "aba")]); // only the first `aba`
I think the most complete answer, based on the OP's requirement, would be:
let v: Vec<_> = "abcXXXabcYYYabc".match_indices("abc").map(|(i, _)|i).collect();
assert_eq!(v, [0,6,12]);
There is a match_indices: https://doc.rust-lang.org/std/string/struct.String.html#method.match_indices
let v: Vec<_> = "abcXXXabcYYYabc".match_indices("abc").collect();
assert_eq!(v, [(0, "abc"), (6, "abc"), (12, "abc")]);

Resources