Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sort HashMap data by value

Tags:

rust

I want to sort HashMap data by value in Rust (e.g., when counting character frequency in a string).

The Python equivalent of what I’m trying to do is:

count = {} for c in text:     count[c] = count.get('c', 0) + 1  sorted_data = sorted(count.items(), key=lambda item: -item[1])  print('Most frequent character in text:', sorted_data[0][0]) 

My corresponding Rust code looks like this:

// Count the frequency of each letter let mut count: HashMap<char, u32> = HashMap::new(); for c in text.to_lowercase().chars() {     *count.entry(c).or_insert(0) += 1; }  // Get a sorted (by field 0 ("count") in reversed order) list of the // most frequently used characters: let mut count_vec: Vec<(&char, &u32)> = count.iter().collect(); count_vec.sort_by(|a, b| b.1.cmp(a.1));  println!("Most frequent character in text: {}", count_vec[0].0); 

Is this idiomatic Rust? Can I construct the count_vec in a way so that it would consume the HashMaps data and owns it (e.g., using map())? Would this be more idomatic?

like image 313
Stefan Scherfke Avatar asked Jan 01 '16 11:01

Stefan Scherfke


People also ask

Can you sort HashMap by value?

HashMaps are a good method for implementing Dictionaries and directories. Key and Value can be of different types (eg - String, Integer). We can sort the entries in a HashMap according to keys as well as values.

Can we sort Map by value in Java?

Example: Sort a map by values Inside the method, we first created a list named capitalList from the map capitals . We then use the sort() method of Collections to sort elements of the list. The sort() method takes two parameters: list to be sorted and a comparator. In our case, the comparator is a lambda expression.

Is it possible to sort the entries in Map based on the values rather than by keys?

Steps to sort HashMap by values One difference between sorting HashMap by keys and values is that it can contain duplicate values by not duplicate keys. You cannot use TreeMap here because it only sorts entries by keys.


2 Answers

Is this idiomatic Rust?

There's nothing particularly unidiomatic, except possibly for the unnecessary full type constraint on count_vec; you could just use

let mut count_vec: Vec<_> = count.iter().collect(); 

It's not difficult from context to work out what the full type of count_vec is. You could also omit the type constraint for count entirely, but then you'd have to play shenanigans with your integer literals to have the correct value type inferred. That is to say, an explicit annotation is eminently reasonable in this case.

The other borderline change you could make if you feel like it would be to use |a, b| a.1.cmp(b.1).reverse() for the sort closure. The Ordering::reverse method just reverses the result so that less-than becomes greater-than, and vice versa. This makes it slightly more obvious that you meant what you wrote, as opposed to accidentally transposing two letters.

Can I construct the count_vec in a way so that it would consume the HashMaps data and owns it?

Not in any meaningful way. Just because HashMap is using memory doesn't mean that memory is in any way compatible with Vec. You could use count.into_iter() to consume the HashMap and move the elements out (as opposed to iterating over pointers), but since both char and u32 are trivially copyable, this doesn't really gain you anything.

like image 132
DK. Avatar answered Sep 19 '22 08:09

DK.


This could be another way to address the matter without the need of an intermediary vector.

// Count the frequency of each letter let mut count: HashMap<char, u32> = HashMap::new(); for c in text.to_lowercase().chars() {     *count.entry(c).or_insert(0) += 1; }  let top_char = count.iter().max_by(|a, b| a.1.cmp(&b.1)).unwrap();  println!("Most frequent character in text: {}", top_char.0); 
like image 32
Juan Vidal Avatar answered Sep 19 '22 08:09

Juan Vidal