I have a Vector
of players to be sorted by the following criteria:
- by
championships
(descending) - by
wins
(descending) - by
name
(ascending)
I implemented it as follows in Rust:
use std::cmp::Ordering;
#[derive(Debug)]
struct Player {
name: String,
championships: u8,
wins: u8,
}
fn main() {
let mut players = vec![
Player {
name: "Alice".to_string(),
championships: 3,
wins: 17,
},
Player {
name: "Bob".to_string(),
championships: 3,
wins: 19,
},
Player {
name: "Claire".to_string(),
championships: 4,
wins: 18,
},
Player {
name: "Dan".to_string(),
championships: 4,
wins: 18,
},
];
players.sort_by(|a, b| match b.championships.cmp(&a.championships) {
Ordering::Less => Ordering::Less,
Ordering::Greater => Ordering::Greater,
Ordering::Equal => match b.wins.cmp(&a.wins) {
Ordering::Less => Ordering::Less,
Ordering::Greater => Ordering::Greater,
Ordering::Equal => match a.name.cmp(&b.name) {
Ordering::Less => Ordering::Less,
Ordering::Greater => Ordering::Greater,
Ordering::Equal => Ordering::Equal,
},
},
});
dbg!(players);
}
This works as intended, as the output shows:
[src/main.rs:46] players = [
Player {
name: "Claire",
championships: 4,
wins: 18,
},
Player {
name: "Dan",
championships: 4,
wins: 18,
},
Player {
name: "Bob",
championships: 3,
wins: 19,
},
Player {
name: "Alice",
championships: 3,
wins: 17,
},
]
However, the pattern matching code is very repetitive, because in most cases I just return what I’ve already got, e.g.:
Ordering::Less => Ordering::Less,
Ordering::Greater => Ordering::Greater,
Is there any shortcut to avoid this kind of verbosity?
It’s not really a sorting problem but a pattern matching problem. (Maybe the sorting by multiple criteria could be done in a more elegant way, which I didn’t figure out how to do, given that I have ascending/descending criteria to sort by.)
>Solution :
Chaining comparisons as in your example is what std::cmp::Ordering::then
(and then_with
) is for:
players.sort_by(|a, b| {
b.championships
.cmp(&a.championships)
.then(b.wins.cmp(&a.wins))
.then(b.name.cmp(&a.name).reverse())
});