views:

177

answers:

5

How can I efficiently and easily sort a list of tuples without being sensitive to case?

For example this:

[('a', 'c'), ('A', 'b'), ('a', 'a'), ('a', 5)]

Should look like this once sorted:

[('a', 5), ('a', 'a'), ('A', 'b'), ('a', 'c')]

The regular lexicographic sort will put 'A' before 'a' and yield this:

[('A', 'b'), ('a', 5), ('a', 'a'), ('a', 'c')]
+7  A: 

You can use sort's key argument to define how you wish to regard each element with respect to sorting:

def lower_if_possible(x):
    try:
        return x.lower()
    except AttributeError:
        return x

L=[('a', 'c'), ('A', 'b'), ('a', 'a'), ('a', 5)]

L.sort(key=lambda x: map(lower_if_possible,x))
print(L)

See http://wiki.python.org/moin/HowTo/Sorting for an explanation of how to use key.

unutbu
Cool, I was trying to figure out how to use key in this situation and using map() didn't occur to me. Thanks!
David Underhill
A: 

Something like this should work:

def sort_ci(items):
    def sort_tuple(tuple):
        return ([lower(x) for x in tuple],) + tuple
    temp = [sort_tuple(tuple) for tuple in items]
    temp.sort()
    return [tuple[1:] for tuple in temp]

In other words, create a new list, where each item is a tuple consisting of the old tuple, prefixed with the same tuple with each item in lower case. Then sort that.

This is a bit faster than using sort's optional comparison function argument, if your list is long.

Lars Wirzenius
+2  A: 
list_of_tuples.sort(key=lambda t : tuple(s.lower() if isinstance(s,basestring) else s for s in t))
Paul McGuire
Nice, compact solution. Thanks!
David Underhill
If your tuples can contain other tuples or sequence structures, then you'll probably want the lower_if_possible solution, wrapped within a recursive caller called something like lower_sequence, which calls itself if it finds a member that is itself a sequence.
Paul McGuire
A: 

Here's a solution which uses the decorator idea illustrated in the "sorted by keys" section of a Python wiki article (http://wiki.python.org/moin/HowTo/Sorting/).

# Create a list of new tuples whose first element is lowercase
# version of the original tuple.  I use an extra function to
# handle tuples which contain non-strings.
f = lambda x : x.lower() if type(x)==str else x
deco = [(tuple(f(e) for e in t), t) for t in ex]

# now we can directly sort deco and get the result we want
deco.sort()

# extract the original tuples in the case-insensitive sorted order
out = [t for _,t in deco]
David Underhill
A: 

A simplified version of Paul McGuires works:

list_of_tuples.sort(key=lambda t : tuple(t[0].lower()))

(where t[0] references which tuple element you want to use, in this case the first)

T1ckL35