Using max_by_key on a vector of floats

2019-01-20 16:39发布

问题:

I want to use max_by_key to get the maximum value from a vector of f64s based on a certain key. This is a simple example, with a small vector and abs as the key:

let a: Vec<f64> = vec![-3.0, 0.2, 1.4];
*a.iter().max_by_key(|n| n.abs()).unwrap()

However, since f64 does not implement Ord, I get

    error[E0277]: the trait bound `f64: std::cmp::Ord` is not satisfied
 --> src/main.rs:3:15
  |
3 |     *a.iter().max_by_key(|n| n.abs()).unwrap();
  |               ^^^^^^^^^^ the trait `std::cmp::Ord` is not implemented for `f64`

Similarly, sort_by_key fails with the same error:

a.sort_by_key(|n| n.abs())

I know I can get around the partial ordering restriction to sort a vector of floats with sort_by

b.sort_by(|m, n| m.partial_cmp(n).unwrap_or(Less))

but that would have to be called on a vector b for which I've computed the key (in this case abs) for each element of a, and then I would have to go back and find the corresponding element of a, which seems complicated and slow. As the number of items in the list grows, I'd like to minimize passes through the data.

Are there any workarounds?

回答1:

If you do not want to create a wrapper type you can use the ordered_float or ord_subset crate. For example

extern crate ordered_float;
extern crate ord_subset;

#[test]
fn test_example_() {
    use ordered_float::OrderedFloat;
    // OrderedFloat -> NaN is greater than all other values and equal to itself.
    // NotNaN -> NotNaN::new panics if called with NaN.

    let mut a: Vec<f64> = vec![-3.0, 0.2, 1.4];

    let max = *a.iter().max_by_key(|n| OrderedFloat(n.abs())).unwrap();
    assert_eq!(-3.0, max);

    a.sort_by_key(|n| OrderedFloat(n.abs()));
    assert_eq!(vec![0.2, 1.4, -3.0], a);
}

#[test]
fn test_example_ord_subset() {
    use ord_subset::OrdSubsetIterExt;

    let a: Vec<f64> = vec![-3.0, 0.2, 1.4];

    // For f64, NaN is ignored.
    let max = *a.iter().ord_subset_max_by_key(|n| n.abs()).unwrap();
    assert_eq!(-3.0, max);

    // ord_subset does not help with the sorting problem in the question
}