<div dir="ltr">Awesome! Thanks for the thorough explanation.</div><div class="gmail_extra"><br><div class="gmail_quote">On Sat, Oct 15, 2016 at 11:01 PM, Tim Peters <span dir="ltr"><<a href="mailto:tim.peters@gmail.com" target="_blank">tim.peters@gmail.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">[Alireza Rafiei <<a href="mailto:alireza.rafiei94@gmail.com">alireza.rafiei94@gmail.com</a>>]<br>
<span class="">> I have a list called count_list which contains tuples like below:<br>
><br>
> > [('bridge', 2), ('fair', 1), ('lady', 1), ('is', 2), ('down', 4),<br>
> > ('london', 2), ('falling', 4), ('my', 1)]<br>
><br>
><br>
> I want to sort it based on the second parameter in descending order and the<br>
> tuples with the same second parameter must be sorted based on their first<br>
> parameter, in alphabetically ascending order. So the ideal output is:<br>
><br>
> > [('down', 4), ('falling', 4), ('bridge', 2), ('is', 2), ('london', 2),<br>
> > ('fair', 1), ('lady', 1), ('my', 1)]<br>
<br>
</span>I'd like to suggest doing something simple instead, such that<br>
<br>
    data = [('bridge', 2), ('fair', 1), ('lady', 1),<br>
<span class="">            ('is', 2), ('down', 4), ('london', 2),<br>
            ('falling', 4), ('my', 1)]<br>
<br>
</span>    from operator import itemgetter<br>
    multisort(data, [# primary key is 2nd element, reversed<br>
                     (itemgetter(1), True),<br>
                     # secondary key is 1st element, natural<br>
                     (itemgetter(0), False)])<br>
    import pprint<br>
    pprint.pprint(data)<br>
<br>
prints the output you want.<br>
<br>
It's actually very easy to do this, but the cost is that it requires<br>
doing a full sort for _each_ field you want to sort on.  Because<br>
Python's sort is stable, it's sufficient to merely sort on the<br>
least-significant key first, and then sort again on each key in turn<br>
through the most-significant.  There's another subtlety that makes<br>
this work:<br>
<br>
> ...<br>
<span class="">> The main problem is that reverse argument takes only a boolean and applies<br>
> to the whole list after sorting in finished.<br>
<br>
</span>Luckily, that's not how `reverse` actually works.  It _first_reverses<br>
the list, then sorts, and then reverses the list again.  The result is<br>
that items that compare equal _retain_ their original order, where<br>
just reversing the sorted list would invert their order.  That's why,<br>
in your example above, after first sorting on the string component in<br>
natural order we get (in part)<br>
<br>
    [[('down', 4), ('falling', 4), ...]<br>
<br>
and then reverse-sorting on the integer portion _leaves_ those tuples<br>
in the same order.  That's essential for this decomposition of the<br>
problem to work.<br>
<br>
With that background, here's the implementation:<br>
<br>
    def multisort(xs, specs):<br>
        for key, reverse in reversed(specs):<br>
            xs.sort(key=key, reverse=reverse)<br>
<br>
That's all it takes!  And it accepts any number of items in `specs`.<br>
Before you worry that it's "too slow", time it on real test data.<br>
`.sort()` is pretty zippy, and this simple approach allows using<br>
simple key functions.  More importantly, it's much easier on your<br>
brain ;-)<br>
</blockquote></div><br></div>