How to sort tuples vector containg one struct and another option struct?

Hi, I want to sort tuples vector containing one struct and another option struct. For example,

struct Car {
    name: String,
    price: f64,
    number_plate: u32,
}

struct Van {
    name: String,
    price: f64,
    number_plate: u32,
}

fn main() {
    let mut vehicles = vec![
        (
            Car {
                name: "car1".to_string(),
                price: 3.0,
                number_plate: 23,
            },
            Some(Van {
                name: "van1".to_string(),
                price: 1.0,
                number_plate: 466,
            })
        ),
        (
            Car {
                name: "car2".to_string(),
                price: 2.2,
                number_plate: 236,
            },
            Some(Van {
                name: "van2".to_string(),
                price: 0.3,
                number_plate: 343,
            })
        )
    ];
    // now how to sort this vehicles tuple by price and number_plate fields??
}

Have you read the documentation?

Yes and I've also tried few things to sort tuples vector like using sort_by, sort_by_key methods but they didn't help. Sorry I should've mentioned this earlier. Nevertheless, this is what I've tried:

#[derive(Debug, Ord, PartialOrd, PartialEq, Eq)]
struct Car {
    name: String,
    price: f64,
    number_plate: u32,
}

#[derive(Debug, Ord, PartialOrd, PartialEq, Eq)]
struct Van {
    name: String,
    price: f64,
    number_plate: u32,
}

fn main() {
    let mut vehicles = vec![
        (
            Car {
                name: "car1".to_string(),
                price: 3.0,
                number_plate: 23,
            },
            Some(Van {
                name: "van1".to_string(),
                price: 1.0,
                number_plate: 466,
            })
        ),
        (
            Car {
                name: "car2".to_string(),
                price: 2.2,
                number_plate: 236,
            },
            Some(Van {
                name: "van2".to_string(),
                price: 0.3,
                number_plate: 343,
            })
        )
    ];
    // trying to sort by price & number_plate keys using `sort_by_key`
    vehicles.sort_by_key(|key| match key {
        (.., Some(b)) => (b.price, b.number_plate),
        _ => (0 as f64, 0),
    });
    println!("After sorting with sort_by_key: {:?}", vehicles);
    // trying to sort by price & number_plate keys using `sort_by`
    vehicles.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap());
    println!("After sorting with sort_by: {:?}", vehicles);
}

With sort_by_key an error is thrown like:

the trait bound `f64: Ord` is not satisfied
the following other types implement trait `Ord`:
  i128
  i16
...
and 4 others
required for `(f64, u32)` to implement `Ord`
required by a bound in `std::slice::<impl [T]>::sort_by_key`

With sort_by sorting seems to work but not key specific intended.
Sorts tuples vector as general, say if I want to sort tuples vector
by "price" key or by both ("price" & "number_plate") of Van struct then how to achieve
that?

The problem why f64 doesn’t implement Ord is that f64::NAN values don’t compare with other numbers.

One possible approach to make the Ord story easier would be to use a wrapper type like this one to rule out NaN values. Another approach would be to use sort_by, and when comparing f64 values to use partial_cmp and unwrap the result. As another alternative, there’s also a total_cmp method, which would order NaN-values after all (becoming smaller than anything else); though I assume that you don’t actually want to keep around prices that aren’t a valid number anyways, so that panicking – or perhaps even better doing some error handling – for this possibility isn’t necessarily the worst idea.


Edit: Actually, another very much quite reasonable alternative is to not use floating point numbers for prices. Prices are typically whole numbers anyways; e.g. a whole number of cents.

3 Likes

Thanks @steffahn for checking. I tried looking other methods too but none of them helped me to solve this sorting issue based on my requirement. Regarding the "price" field I was giving an example of floating point type but you can assume any field with type float 32/64 for this case. To be honest, I'm a bit new in rust and I was really looking for short hand working code snippets here. So would be glad if any working code snippets can be seen.

Also, most of examples for sorting have given (or can be found) in the internet about vector of structs with single level sort. Here in this case, I need to sort tuples of structs having one of option type and sorting should be done based on option type struct's key fields i.e. on multi-level sorting.

Adapting your sort_by_key attempt Rust Playground

Same logic, no external crate, using sort_by Rust Playground

Using sort_by, but a non-panicking-on-nan approach with total_cmp Rust Playground

3 Likes

Thanks so much @steffahn for your help. All of them works like a charm.