Posts to read

- BASH Script to clean duplicate filesBooks to Sell in MunichCompiling Stockfish on Raspberry PIGenerating Random Strings and Save them SortedHow to secure your Nginx HTTP Server using Basic HTTP AuthenticationInstalling PostgreSQL 13 on Ubuntu EC2 InstanceInstalling PostgreSQL 14 on Ubuntu ServerInstalling Ruby on Rails Development Environment on Mac OSJQ TutorialMy Five Years Learnings at FreeleticsOpenSearch TutorialPost to Twitter using PythonPostgreSQL 13 Streaming Replication on UbuntuPython Cheat SheetRedis Streams TutorialRunning your own Docker Registry on UbuntuSorting in PythonThoughts on Health and Diet

Homepage

In this post I want to show some quick instructions for sorting in Python. Obviously I am talking about Python 3.

Let's start with sorting numbers. Let's say you have a list of numbers

`list = [5,7,1,3,4,10,2.5]`

To get the list sorted, you can call the function **sorted** like this

```
sorted_list = sorted(list)
print(sorted_list)
[1, 2.5, 3, 4, 5, 7, 10]
```

You can also sort the numbers in **descending order**. Simply by passing the **reverse** parameter and setting it to **true**

```
sorted_list = sorted(list, reverse=True)
print(sorted_list)
[10, 7, 5, 4, 3, 2.5, 1]
```

You can also do the same with strings as well.

```
words = ["mountains", "are", "beautiful"]
sorted_words = sorted(words, reverse=True)
print(sorted_words)
['mountains', 'beautiful', 'are']
```

Now let's make it more complicated. Let's say you have a list of **lists**, and you need to sort these
lists based on the value in the second index (index = 1)

`lists = [[2,10], [1,3], [0,5]]`

You can achieve by passing by passing a lambda to the **key**

```
sorted(lists, key=lambda x: x[1])
[[1, 3], [0, 5], [2, 10]]
```

But if you want to achieve it without writing your own lambda, the **operator** module
already provides you with such lambda(s)

```
from operator import itemgetter
sorted(lists, key=itemgetter(1))
[[1, 3], [0, 5], [2, 10]]
```

Now let's make it more complicated, and try to sort class objects. Let's go ahead
and define a **Student** class that has a name, and scores math and english

```
class Student:
def __init__(self, name, math, english):
self.name = name
self.math = math
self.english = english
def __repr__(self):
return self.name
```

then let's create two objects of this class

`students = [Student("omar", 10, 20), Student("john", 20, 5)]`

To sort the students based on some attribute, we can use the operator module **attrgetter**.

```
from operator import attrgetter
sorted_math = sorted(students, key=attrgetter("math"))
print(sorted_math)
[omar, john]
```

The same thing can be done for both the **english** and **name** attributes

```
sorted_english = sorted(students, key=attrgetter("english"))
print(sorted_english)
[john, omar]
```

```
sorted_name = sorted(students, key=attrgetter("name"))
print(sorted_name)
[john, omar]
```

Now let's make the requirements much harder. Let's say that you want to sort a list
of **Student** objects, to satisfy these requirements:
1. Students with the name **omar** should appear first.
2. Students without the name **omar** should appear afterwards.
3. Students with name **omar** should be sorted according to their math grade in descending order.
4. Other students are sorted according to their name, then math grade in ascending order. Name has more priority.

So the sorted list should look something like this

`[ < students with name omar sorted in descending order according to math grade > , < students with name != omar sorted in ascending order according to their (name, math) grade >]`

I hope you don't hate me for this. To achieve this, we have to write our own comparing algorithm, that will be fed into the
sorting algorithm. The algorithm (function) will be used to compare two objects. The sorting method **sorted** will take care of the rest.

Let's start by importing **functools** module. Because we need to use its **cmp_to_key** method.
Then lets redefine the **Student** class, because we need to show the **math** grade now in the string
representation of each student object.

`import functools`

```
class Student:
def __init__(self, name, math, english):
self.name = name
self.math = math
self.english = english
def __repr__(self):
return f"{self.name}/{self.math}"
```

Now we need to write our own **comparison** method. It goes like this

```
def my_compare_function(student1, student2):
if student1.name == student2.name:
if student1.name == "omar":
return 1 if student1.math < student2.math else (0 if student1.math == student2.math else -1)
else:
return -1 if student1.math < student2.math else (0 if student1.math == student2.math else 1)
else:
if student1.name == "omar":
return -1
elif student2.name == "omar":
return 1
else:
if student1.name < student2.name:
return -1
else:
return 1
```

This method always takes two objects to compare. In our case two student objects.
It needs to return **-1** if student1 should appear before student2. **1** if student2 should appear first.
It needs to return **0** if they are equal. In our case the **name** and the **math** grade are equal. To preserve
the order of the student objects in the list, and not reorder them.

Finally, we need to pass this **comparison** method to the **functools.cmp_to_key** method, and sort the students.

```
students = [Student("omar", 10, 20), Student("john", 20, 5), Student("omar", 1, 20), Student("omar", 2, 20), Student("zoey", 10, 20), Student("hany", 20, 4), Student("hany", 3, 10)]
print(sorted(students, key=functools.cmp_to_key(my_compare_function)))
```

which should print

`[omar/10, omar/2, omar/1, hany/3, hany/20, john/20, zoey/10]`

As expected.

I hope this was useful. Write me on Twitter @OmarQunsul if something is not clear. I would love to hear back from you.

Homepage