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