How to sort items inside a vector
Asked Answered
K

2

6

I have a vector [(5, 1), (9, 1), (4, 2)]

I want it to be sorted as [(4, 2), (9, 1), (5, 1)]

Which is sorted in descending order by every second element then the first element. Would it be possible using sort_by(|x, y| y.cmp(x)) function?

Kingsley answered 30/1, 2022 at 22:51 Comment(0)
E
4

Here is one way to do it:

ve.sort_unstable_by(|a, b| (b.1, b.0).cmp(&(a.1, a.0)));

Note that I used sort_unstable_by(), which, in your case, would have the same effect as sort_by() but is a bit faster and uses less memory.

Silvio's suggestion could also be slightly optimized by using sort_unstable_by:

ve.sort_unstable_by(|x, y| y.1.cmp(&x.1).then(y.0.cmp(&x.0)));

And here is one more way that comes to mind:

ve.sort_unstable_by_key(|k| (-k.1, -k.0));
Evert answered 31/1, 2022 at 6:51 Comment(2)
This is a good approach because Rust already knows how to compare tuples, no need to repeat it. Also, this works for non-Copy types as well: |a, b| (&b.1, &b.0).cmp(&(&a.1, &a.0))Semasiology
I'd personally go with ve.sort_unstable_by_key(|&(x, y)| Reverse((y, x))), using std::cmp::Reverse. Using negation to sort in reverse order seems like a bit of a hack.Fruitcake
B
6

It is most certainly possible, and you have the right idea using sort_by. You can sort by the second element in basically the way you suggest.

sort_by(|x, y| y.1.cmp(&x.1))

This solves the first part of your problem. To handle the tiebreaker, we want to be able to say "if the second element is the same, then go by the first element", and in fact Rust has a method that does exactly this: Ordering::then.

sort_by(|x, y| y.1.cmp(&x.1).then(y.0.cmp(&x.0)))

Note: This will evaluate both orderings in every case, which is probably fine. If your comparison function is unusually expensive, you might consider then_with instead, which takes a closure rather than a value as its argument.

Try it online!

Braiding answered 30/1, 2022 at 22:59 Comment(1)
When using Copy types, I'd recommend using v.sort_by_key(|&(_, x)| std::cmp::Reverse(x)); instead, which is clearer. (For non-Copy types, sort_by_key has some issues with lifetimes and would be harder to use)Cudgel
E
4

Here is one way to do it:

ve.sort_unstable_by(|a, b| (b.1, b.0).cmp(&(a.1, a.0)));

Note that I used sort_unstable_by(), which, in your case, would have the same effect as sort_by() but is a bit faster and uses less memory.

Silvio's suggestion could also be slightly optimized by using sort_unstable_by:

ve.sort_unstable_by(|x, y| y.1.cmp(&x.1).then(y.0.cmp(&x.0)));

And here is one more way that comes to mind:

ve.sort_unstable_by_key(|k| (-k.1, -k.0));
Evert answered 31/1, 2022 at 6:51 Comment(2)
This is a good approach because Rust already knows how to compare tuples, no need to repeat it. Also, this works for non-Copy types as well: |a, b| (&b.1, &b.0).cmp(&(&a.1, &a.0))Semasiology
I'd personally go with ve.sort_unstable_by_key(|&(x, y)| Reverse((y, x))), using std::cmp::Reverse. Using negation to sort in reverse order seems like a bit of a hack.Fruitcake

© 2022 - 2024 — McMap. All rights reserved.