Welcome, guest | Sign In | My Account | Store | Cart

Here we use the properties of "cmp()" and "or" to produce a compact dialect for sorting a list.

Python, 48 lines
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import string

star_list = ['Elizabeth Taylor',
             'Bette Davis',
             'Hugh Grant',
             'C. Grant']

star_list.sort(lambda x,y: (
   cmp(string.split(x)[-1], string.split(y)[-1]) or  # Sort by last name ...
   cmp(x, y)))                                       #  ... then by first name

print "Sorted list of stars:"
for name in star_list: 
    print name

#
# "cmp(X, Y)" return 'false' (0) when X and Y compare equal,
# so "or" makes the next "cmp()" to be evaluated.
# To reverse the sorting order, simply swap X and Y in cmp().
#
# This can also be used if we have some other sorting criteria associated with
# the elements of the list.  We simply build an auxiliary list of tuples
# to pack the sorting criteria together with the main elements, then sort and
# unpack the result.
#

def sorting_criterium_1(data):
    return string.split(data)[-1]   # This is again the last name.

def sorting_criterium_2(data):
    return len(data)                # This is some fancy sorting criterium.

# Pack the auxiliary list:
aux_list = map(lambda x: (x, 
                          sorting_criterium_1(x), 
                          sorting_criterium_2(x)),
               star_list)
# Sort:
aux_list.sort(lambda x,y: (
   cmp(x[1], y[1])  or       # Sort by criteria 1 (last name)...
   cmp(y[2], x[2])  or       #  ... then by criteria 2 (in reverse order) ...
   cmp(x, y)))               #  ... then by the value in the main list.
# Unpack the resulting list:
star_list = map(lambda x: x[0], aux_list)

print "Another sorted list of stars:"
for name in star_list: 
    print name

1 comment

Alex Martelli 22 years, 6 months ago  # | flag

decorate-sort-undecorate idiom does it better. dsu variant of this (faster, clearer):

# "decorate" == Pack the auxiliary list:
aux_list=[(sort_crit1(x),sort_crit2(x),x) for x in star_list]
# "sort" == JUST sort, no wasted effort/complication
aux_list.sort()
# "undecorate" == Unpack the resulting list:
star_list = [x[-1] for x in aux_list]

This doesn't easily do "reverse" sorting on some of the fields while being "direct" on others. On number criteria, you can just change sign (use -sort_critN(x)). On string criteria, you need a string map that swaps chr(x) with chr(255-x) for x in range(128), or wider if Unicode (but you only need to write that once).