Sorting Techniques
Link to article
Sorting Basics
There are two basic ways we can ascending sort (organize from least to greatest on value) built into Python, .sort()
on a list object, and the sorted()
function, which takes in any iterable sequence.
sorted()
will actually return the sequence in order, while .sort()
is more of a step to be done, and will actually return None
.
>>> a = [3, 6, 1, 4]
>>> sorted(a)
[1, 3, 4, 6]
>>> a.sort()
>>> a
[1, 3, 4, 6]
Key Functions
Both of the methods we looked at above have an additional parameter key
, which takes in a function that is performed on each element within the sorted list before making any comparisons. This allows for different sorting methods; some even use lambda which is pretty cool.
# Case Insensitve
>>> sorted('I\'m like, a real Loughlin Lion!'.split(), key=str.casefold)
['a', "I'm", 'like,', 'Lion!', 'Loughlin', 'real']
# Using Lambda on tuples to sort by age
>>> student_tuples = [
... ('Leyla', 'A', 15),
... ('Jayden', 'B', 12),
... ('Myles', 'B', 10),
... ]
>>> sorted(student_tuples, key=lambda student: student[2])
[('Myles', 'B', 10), ('Jayden', 'B', 12), ('Leyla', 'A', 15)]
# Using Lambda to sort on named attributes
>>> class Student:
... def __init__(self, name, grade, age):
... self.name = name
... self.grade = grade
... self.age = age
... def __repr__(self):
... return repr((self.name, self.grade, self.age))
>>> student_objects = [
... Student('Leyla', 'A', 15),
... Student('Jayden', 'B', 12),
... Student('Myles', 'B', 10),
... ]
>>> sorted(student_objects, key=lambda student: student.age)
[('Myles', 'B', 10), ('Jayden', 'B', 12), ('Leyla', 'A', 15)]
Using functions from the operator
module, we can actually perform the above two examples even easier, allowing for even multiple levels of sorting;
>>> from operator import itemgetter, attrgetter
# Using the student tuples from above
>>> sorted(student_tuples, key=itemgetter(2))
[('Myles', 'B', 10), ('Jayden', 'B', 12), ('Leyla', 'A', 15)]
# Using the student objects from above
>>> sorted(student_objects, key=attrgetter('age'))
[('Myles', 'B', 10), ('Jayden', 'B', 12), ('Leyla', 'A', 15)]
# Sort by grade then sort by age
>>> sorted(student_objects, key=attrgetter('grade', 'age'))
[('Leyla', 'A', 15), ('Myles', 'B', 10), ('Jayden', 'B', 12)]
Descending
Simply put, if you set reverse
to True
in either .sort()
or sorted()
, you can sort in descending order versus the usual ascending order.
>>> sorted(student_objects, key=attrgetter('age'), reverse=True)
[('Leyla', 'A', 15), ('Jayden', 'B', 12), ('Myles', 'B', 10)]
Sort Stability
Python’s sorts are called stable sorts, because items keep their existing order when sorted. This can allow for some complex sorts, and even things know as multi-sorts, which are just multiple sorts on top of one another. Below are some neat examples of how these can be achieved.
# An example of stability; note blue's order
>>> data = [('red', 1), ('blue', 1), ('red', 2), ('blue', 2)]
>>> sorted(data, key=itemgetter(0))
[('blue', 1), ('blue', 2), ('red', 1), ('red', 2)]
# Creating a multisort function
>>> def multisort(list_to_be_sorted, specs):
... for key, reverse in reversed(specs):
... list_to_be_sorted.sort(key=attrgetter(key), reverse=reverse)
... return list_to_be_sorted
>>> multisort(list(student_objects), (('grade', True), ('age', False)))
[('Myles', 'B', 10), ('Jaiden', 'B', 12), ('Leyla', 'A', 15)]