2

I'm using Python 3.8. I am trying to sort players by points (in descending order) and then, only if they have the same number of points, by rank.

I've already read Sorting HOW TO, Python sorting by multiple criteria and Sorting by multiple conditions in python.

Here's my code:

from operator import itemgetter

players_results = [
    ("Pierre", 1.0, 1),
    ("Matthieu", 1.0, 2),
    ("Joseph", 0.5, 3),
    ("Marie", 0.0, 4),
    ("Michel", 0.5, 5),
    ("Raphael", 0.0, 6),
]

sorted_by_points = sorted(players_results, key=itemgetter(1), reverse=True)
points_descending_rank_ascending = sorted(sorted_by_points, key=itemgetter(2))
print(points_descending_rank_ascending)

# [('Pierre', 1.0, 1), ('Matthieu', 1.0, 2), ('Joseph', 0.5, 3), ('Marie', 0.0, 4), ('Michel', 0.5, 5), ('Raphael', 0.0, 6)]

In each tuple, the number of points are of float type while the rank is of integer type. My problem is that Michel should be before Marie, but that's not the case. Can someone explain what I have to implement differently?

jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
ClGide
  • 43
  • 5
  • Answers on both of the SO questions you link suggest sorting on a tuple key, which you're **not** doing. You're sorting twice, not using multiple criteria simultaneously. – jonrsharpe Dec 07 '21 at 17:02
  • Does this answer your question? [Python sorting by multiple criteria](https://stackoverflow.com/questions/20145842/python-sorting-by-multiple-criteria) – jonrsharpe Dec 07 '21 at 17:04
  • " if all the other criteria are numeric, then negate their values for the sort key and add reverse=True. Otherwise, use consecutive sorts, using the rightmost criteria first, then the next, etc" Martijn Peters. What should I do if the values aren't numeric and I want to combine an ascending and a descending criteria ? – ClGide Dec 07 '21 at 17:11
  • _"What should I do if the values aren't numeric..."_ - why do you ask? The values you want to sort on *are* numeric. – jonrsharpe Dec 07 '21 at 17:12
  • @jonrsharpe you're right, but I may need it later in my project – ClGide Dec 07 '21 at 17:15
  • In the absence of a concrete example, it's hard to say exactly what you should do, so you'd be better off waiting for that need to actually materialise (if it ever does) to investigate it. If you wanted some string fields to be sorted alphabetically and others reverse-alphabetically, for example, that would be quite complicated (compared to numerical fields, where it's easy to negate them). – jonrsharpe Dec 07 '21 at 17:16

3 Answers3

2

One solution could be:

sorted(players_results, key=lambda x:(-x[1],x[2]))

OUTPUT

[('Pierre', 1.0, 1), ('Matthieu', 1.0, 2), ('Joseph', 0.5, 3), ('Michel', 0.5, 5), ('Marie', 0.0, 4), ('Raphael', 0.0, 6)]
nikeros
  • 3,302
  • 2
  • 10
  • 26
0

I suggest using 3rd party libraries, such as pandas:

import pandas as pd

players_results = [
    ("Pierre", 1.0, 1),
    ("Matthieu", 1.0, 2),
    ("Joseph", 0.5, 3),
    ("Marie", 0.0, 4),
    ("Michel", 0.5, 5),
    ("Raphael", 0.0, 6),
]
df = pd.DataFrame(players_results)
df.columns = ["name", "points", "rank"]
df.sort_values(["points", "rank", "name"], ascending=[False, True, True])

result

rezan21
  • 1,065
  • 10
  • 14
-1

Attemping to respect your script, something like this works:

if __name__ == '__main__':

    players_results = [
        ("Pierre", 1.0, 1),
        ("Matthieu", 1.0, 2),
        ("Joseph", 0.5, 3),
        ("Marie", 0.0, 4),
        ("Michel", 0.5, 5),
        ("Raphael", 0.0, 6),
    ]


    # In one line:
    points_descending_rank_ascending = sorted(players_results, key=lambda t: (-t[1], t[2]))
    print(points_descending_rank_ascending)
Winter Squad
  • 169
  • 7