Sorting parallel lists in python - first item descending, second item ascending
<p>I have two lists - one for a team name and the second for points </p>

<p>I wanted to sort them based on the points without losing track of the corresponding team. I found something on SO that works great for doing this - and it sorts teams by points in descending order (most to least).</p>

<p>There doesn't appear to be a sort on the second list.</p>

<blockquote>
  <p><strong>EDIT - Original Data</strong></p>
</blockquote>

<pre><code>Teams       Pts
D Clark   0
D Dupuis   2
T Leach   2
J Schutz    0
C Hearder 2
R Pagani  0
M Cameron  2
B Hunter  0
</code></pre>

<p><strong>This is what I'm using:</strong></p>

<pre><code>pts, teams = zip(*sorted(zip(pts, teams), reverse=True))
i = 1
for team, num_pts in zip(teams, pts):
    standings = str(i) + '. ' + team + " ("  + num_pts + ")"
    print standings
    i += 1
</code></pre>

<p><strong>And it results in this:</strong>   </p>

<pre><code>1. T Leach (2)
2. M Cameron (2)
3. D Dupuis (2)
4. C Hearder (2)
5. R Pagani (0)
6. J Schutz (0)
7. D Clark (0)
8. B Hunter (0)
</code></pre>

<p>What I'm trying to do is to add an additional sort on team name - so if there's a tie, then it will <em>sort by names in ascending</em> order, </p>

<p><strong>Like This:</strong>   </p>

<pre><code>1. C Hearder (2)
2. D Dupuis (2)
3. M Cameron (2)
4. T Leach (2)
5. B Hunter (0)
6. D Clark (0)
7. J Schutz (0)
8. R Pagani (0)
</code></pre>

<p>You have two options here. You could use <code>cmp</code> or <code>key</code> as a parameter to <code>sorted</code>.</p>

<p>With a <code>cmp</code> you're defining a function which takes two arguments, the two values being compared. If the first argument is smaller than the second, return a negative number. If it is larger than the second, return a positive number. If they are equal, return 0.</p>

<p>First example using <code>cmp</code>:</p>

<pre><code>pts = ['2', '2', '2', '2', '2', '0', '0', '0', '0']
teams = ['T Leach', 'M Cameron', 'D Dupuis', 'C Hearder', 'R Pagani',
         'J Schutz', 'D Clark', 'B Hunter']

def custom_cmp(x,y):
    return cmp(x[0], y[0]) or cmp(y[1], x[1])

pts, teams = zip(*sorted(zip(pts, teams), cmp=custom_cmp, reverse=True))
</code></pre>

<p>The python <code>or</code> operator will return the left hand side if it evaluates to <code>True</code> (not <code>0</code> as described above), otherwise it will return the right hand side. Therefore this <code>custom_cmp</code> returns the comparison of the points from <code>x</code> to <code>y</code> if the points are not equivalent. Otherwise it compares the teams from <code>y</code> to <code>x</code> (the opposite order).</p>

<p>With <code>key</code> on the other hand you are just considering how to view an element individually in order that python will naturally understand how to sort it how you want. We know the first item is being compared properly, so now we just need to flip the second. Numbers would be easy, we could just take the negative. But how do you flip a string? </p>

<p>One approach is to take the <code>ord</code> (which should be an ascii value integer for your strings) of each character in the string. Then you can take the negative of <strong>these</strong> values and package them into a <code>tuple</code>. This is what one of these would look like:</p>

<pre><code>&gt;&gt;&gt; tuple(-ord(c) for c in 'C Hearder')
(-67, -32, -72, -101, -97, -114, -100, -101, -114)
</code></pre>

<p>Second example using <code>key</code>:</p>

<pre><code>def custom_key(pair):
    pts, team = pair
    return (pts, tuple(-ord(c) for c in team))

pts, teams = zip(*sorted(zip(pts, teams), key=custom_key, reverse=True))
</code></pre>

<p><strong>Restrospective edit:</strong></p>

<p>If we're defining a custom <code>key</code> we may as well invert the <code>key</code> itself and remove the <code>reverse</code> argument:</p>

<pre><code>def custom_key(pair):
    pts, team = pair
    return (-int(pts), team)

pts, teams = zip(*sorted(zip(pts, teams), key=custom_key))
</code></pre>

python list python-2.7 sorting