Skip to content

Instantly share code, notes, and snippets.

@jduan
Last active December 1, 2019 20:25
Show Gist options
  • Save jduan/3253496363670d99da434d65d3aa15d9 to your computer and use it in GitHub Desktop.
Save jduan/3253496363670d99da434d65d3aa15d9 to your computer and use it in GitHub Desktop.
Find the top occurring elements from a vector
// Find the top occurring elements from a vector.
// This is how to special a type parameter that implements multiple traits.
fn top_ten<T: Debug + Hash + Eq>(values: &Vec<T>) -> Vec<&T> {
let mut map = HashMap::new();
for value in values {
let counter = map.entry(value).or_insert(0);
*counter += 1;
}
let mut map_vec: Vec<_> = map.into_iter().collect();
map_vec.sort_by(|a, b| b.1.cmp(&a.1));
map_vec.into_iter().map(|a| a.0).take(10).collect()
}
@airbnb-gps
Copy link

I think a lot of this code could be cleaned up through better use of Iterator and its various methods. For example, there is a take(usize) method that will return an iterator of the first N items. You could do something like map_vec.iter().map(|x| *a.0).take(10).collect();

The itertools crate is also pretty handy. If you use itertools::Itertools, it allows the compiler to deduce the types for .collect() so you don't have to annotate explicitly.

Use of the * operator in Rust is typically uncommon. But I think its usage here is fine, as you are dereferencing a ref to a primitive integer type, which triggers the equivalent of copy by value semantics (I believe). If you tried to dereference a struct, the borrow checker might complain!

Also, instead of passing &Vec<T> to a function, pass &[T] instead: this is more flexible since any iterable of T can be passed. You also may want to return a Vec<T> instead of Vec<&T> so you don't have to worry about lifetimes. This will mean extra memory copies. But unless the code is highly performance sensitive, it probably doesn't matter.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment