简体   繁体   中英

Python sorting by the max of the first element, then the min of the second element

I was working on a geometry project in python and I came across an intriguing problem. Assume I have a list of Cartesian coordinates like this:

[[1, 0], [3, 0], [2, 1], [3.5, 2], [4, 3], [6, 1], [6.5, 0], [10, 2], [10, 5], [9, 3.5], [7, 3], [9, 7], [7, 8], [5, 7], [4, 5], [2, 8], [1, 7], [0, 5], [1, 3], [0, 2]]

What would be the best way to sort this list by descending y-value THEN increasing x-value if two coordinates exist with the same y-value? The output should be:

[[2, 8], [7, 8], [1, 7], [5, 7], [9, 7], [0, 5], [4, 5], [10, 5], [9, 3.5], [1, 3], [4, 3], [7, 3], [0, 2], [3.5, 2], [10, 2], [2, 1], [6, 1], [1, 0], [3, 0], [6.5, 0]]

You can supply a key function to list.sort to determine what value each item in your list should be mapped to for sorting. In your case, you can use a tuple of the negative Y-coordinate, and the X-coordinate to get your desired result:

>>> coordinates = [[1, 0], [3, 0], [2, 1], [3.5, 2], [4, 3], [6, 1], [6.5, 0], [10, 2], [10, 5], [9, 3.5], [7, 3], [9, 7], [7, 8], [5, 7], [4, 5], [2, 8], [1, 7], [0, 5], [1, 3], [0, 2]]
>>> coordinates.sort(key=lambda x: (-x[1], x[0]))
>>> coordinates
[[2, 8], [7, 8], [1, 7], [5, 7], [9, 7], [0, 5], [4, 5], [10, 5], [9, 3.5], [1, 3], [4, 3], [7, 3], [0, 2], [3.5, 2], [10, 2], [2, 1], [6, 1], [1, 0], [3, 0], [6.5, 0]]

Poke's answer is the right answer in most cases.

Just in case you are doing this on Python prior to Python 2.4 or if the comparison function is really expensive, you can use Decorate-Sort-Undecorate (DSU or the Schwartzian transform )

>>> coord = [[1, 0], [3, 0], [2, 1], [3.5, 2], [4, 3], [6, 1], [6.5, 0], [10, 2], [10, 5], [
9, 3.5], [7, 3], [9, 7], [7, 8], [5, 7], [4, 5], [2, 8], [1, 7], [0, 5], [1, 3], [0, 2]]
>>> decorated=[[(-y,x),x,y] for x,y in coord]
>>> [[x,y] for t,x,y in sorted(decorated)]
[[2, 8], [7, 8], [1, 7], [5, 7], [9, 7], [0, 5], [4, 5], [10, 5], [9, 3.5], [1, 3], [4, 3], [7, 3], [0, 2], [3.5, 2], [10, 2], [2, 1], [6, 1], [1, 0], [3, 0], [6.5, 0]]

Or:

>>> [[x,y] for t,x,y in sorted([[(-y,x),x,y] for x,y in coord])]
[[2, 8], [7, 8], [1, 7], [5, 7], [9, 7], [0, 5], [4, 5], [10, 5], [9, 3.5], [1, 3], [4, 3], [7, 3], [0, 2], [3.5, 2], [10, 2], [2, 1], [6, 1], [1, 0], [3, 0], [6.5, 0]]

These techniques (and more) are in the Python sorting HowTo

Using sorted and specifying the sorting criterion with a lambda function using a tuple:

>>> coord = [[1, 0], [3, 0], [2, 1], [3.5, 2], [4, 3], [6, 1], [6.5, 0], [10, 2], [10, 5], [9, 3.5], [7, 3], [9, 7], [7, 8], [5, 7], [4, 5], [2, 8], [1, 7], [0, 5], [1, 3], [0, 2]]
>>> sorted(coord, key=lambda x: (-x[1], x[0]))
[[2, 8], [7, 8], [1, 7], [5, 7], [9, 7], [0, 5], [4, 5], [10, 5], [9, 3.5], [1, 3], [4, 3], [7, 3], [0, 2], [3.5, 2], [10, 2], [2, 1], [6, 1], [1, 0], [3, 0], [6.5, 0]]

The order in the lambda function tuple defines the order by which the criterion are considered.

x[1] : sort by second element in ascending order

-x[1] : sort by second element in descending order

x[0] : sort by first element in ascending order

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM