Categories
Python

How To Sort A List In Python Effortlessly

Python has inbuilt function sorted(), which can be use to sort any iterable. In this article we will explore from basics to advance features of sorted() function.

As a programmer, we all have the basic knowledge and know-how of sorting. So keeping that in mind we will not deep dive into the theory part and move directly to the implementational aspects of sorting.

Largely speaking, there are a number of ways we can implement sorting on any given iterable. And the sorting algorithms vary on the basis of the type of problem at hand. Moreover, the complexity of some algorithms is such that most of the developers find it difficult to implement the right algorithm. As a result we end up searching and wasting our time for the right way to sort our iterable.

In this article, we aim to provide some of the most popular and time savvy ways to implement sorting on any iterable.

1. Sorting a list (by default ascending order)

First up, is our one of the most common inbuilt function sorted() provided by python. sorted() can be used to sort any iterable. Moreover, it returns the new sorted iterable unlike sort()  function which performs in-place sorting.

l  = [23,12,35,100,2,43]
#sort list in increasing order
print(sorted(l)) # logs: [2,12,23,35,43,100]

2. Sorting a list in descending order

Now let’s say we wanted to sort a list in decreasing order. For this purpose, you can simply pass reverse=True in the sorted() function as a parameter. And voila !! The iterable gets sorted in the decreasing order without any hassle. Although keep in mind, in the sorted() function, by default reverse is set to False.

#sorting list in decreasing order
print(sorted(l,reverse = True)) # logs: [100, 43, 35, 23, 12, 2]

3. Sorting a list (after applying a logic/expression)

How many times we have faced a problem, where the data we need to sort requires a little tweak before it is sorted ?

I am sure, many of us would resort to implementing it in parts which is not such a bad idea. But with sorted() function available with us, we can perform the same task in a single line.

Workingsorted() function accepts an optional key parameter which can be assigned any logic(expression). The logic is then applied on every element of the list and finally, the list is sorted on the updated values.

To demonstrate above concept, let’s consider a problem statement where we want to sort a list on the basis of modulo 10 (% 10) value.

#sorting list on some condition ex % 10 value
print(sorted(l,key=lambda x: x % 10)) #logs: [100, 12, 2, 23, 43, 35]

Here we have passed a lambda function which converts every value to %10. In other words, we are are telling the sorted() function to sort the iterable on the basis of their modulo 10 value.

In the same way, we can sort the iterable in ascending order without using reverse=True.

print(sorted(l,key=lambda num: -num))
# logs: [100, 43, 35, 23, 12, 2]

4. Sorting a list (after applying a predefined function)

Now we know that we sorted() function accepts a key parameter which can be used to apply a logic/expression. But what if we want to keep the logic separately for reusability purpose or maybe just to improve readability.

For this very purpose, the key parameter of sorted() function also accepts a predefined function. To explain, we can keep our logic in our predefined function and pass our function to the key parameter.

But keep in mind that our predefined function passed to the key parameter can only be assigned one argument.

# this function return average score
def get_average(scores):
    return sum(scores)/len(scores)
# list contains 3 scores of 4 students
test_scores = [(23,45,98),(12,100,0),(33,33,10),(23,45,57)] 
print(sorted(test_scores,key= get_average)
#logs: [(33, 33, 10), (12, 100, 0), (23, 45, 57), (23, 45, 98)]

In above example, we have declared get_average() as our predefined function. As it’s name suggests, get_average() function applies the logic to find average of scores provided and returns the result. And finally, we assign our get_average() function to the key parameter to get a list sorted on the basis of average score.

Of course you don’t need to defined the get_average() function, you can directly pass the logic to sorted function using lambda. But sometimes you will want to keep you logic in a separate place which now you will know how to implement.

print(sorted(test_scores,key=lambda scores:sum(scores)/len(scores)))

We can sort the iterable in descending order with as well as without using reverse=True.

sorted(test_scores,key=lambda scores:-sum(scores)/len(scores))
sorted(test_scores,reverse = True,key= lambda scores: sum(scores)/len(scores))
#both logs: [(23, 45, 98), (23, 45, 57), (12, 100, 0), (33, 33, 10)]

5. Sorting a list of objects

We have gone through most of the sorting operations possible with lists except the one where we have a list of multiple objects. So let’s see how to sort a list of objects on basis of a particular field, shall we?

Sorting example

Firstly, we created a Student class which has 4 fields namely name, math_score, pyhsics_score and chemistry_score. And then we added records of 4 students with unique names and scores to a list called students.

Sorting a list of objects on the basis of a single field

Now let’s consider the above data to sort students on basis of their name attribute.

sorted_result = sorted(students,key = lambda student: getattr(student,'name'))
[print(student) for student in sorted_result]
#logs ---------------------------------------
Name: Baba, marks: [m: 23, P: 45, C: 57]
Name: John, marks: [m: 23, P: 45, C: 98]
Name: Wick, marks: [m: 33, P: 33, C: 10]
Name: Yaga, marks: [m: 12, P: 100, C: 0]

Simply put, in above code, we used getattr() function to fetch the name field value and then we just assigned it to the key parameter. However we could have used student.name instead of getattr() function. Either way, as evident, the sorted() function now sorts the students list on every student’s name in alphabetical order.

Sorting a list of objects on basis of multiple fields

Now let’s move forward to see how we can sort the student records on basis of more than one field.

sorted_result = sorted(students,key = lambda student: (student.math_score,student.physics_score,student.chemistry_score))
[print(student) for student in sorted_result]
#logs -----------------------------------------
Name: Yaga, marks: [m: 12, P: 100, C: 0]
Name: Baba, marks: [m: 23, P: 45, C: 57]
Name: John, marks: [m: 23, P: 45, C: 98]
Name: Wick, marks: [m: 33, P: 33, C: 10]

So let’s find out what happened in above piece of code. We have first sorted the list of students on the basis of their math score. Moreover if the math_score is same then it uses the physics_score and if physics_score is also equal then it uses the chemistry_score.

Sorting a list of objects on basis of a logic/expression

We already know how to provide a logic to the key parameter of sorted() function to sort on the basis of any logic/expression. In the same way, now we are going to sort the records on the basis of average marks :-

Templatelambda x : (first_attr,sec_attr,…..)

def get_average(*score):
    return sum(score)/len(score)
sorted_result = sorted(students,key = lambda student:     get_average(student.math_score,student.physics_score,student.chemistry_score))
[print(student) for student in sorted_result]
#logs --------------------------------------------------
Name: Wick, marks: [m: 33, P: 33, C: 10]
Name: Yaga, marks: [m: 12, P: 100, C: 0]
Name: Baba, marks: [m: 23, P: 45, C: 57]
Name: John, marks: [m: 23, P: 45, C: 98]

What if, we wanted to sort our list in different orders for different conditions?

To demonstrate the same, let’s sort the above data in ascending order of math_score and if math_score is same then in descending order of chemistry_score.

Templatelambda x : (first_attr,sec_attr,…..)

sorted_result = sorted(students,key = lambda student:(student.math_score,-student.chemistry_score))
[print(student) for student in sorted_result]
#logs -------------------------------------------
Name: Yaga, marks: [m: 12, P: 100, C: 0]
Name: John, marks: [m: 23, P: 45, C: 98]
Name: Baba, marks: [m: 23, P: 45, C: 57]
Name: Wick, marks: [m: 33, P: 33, C: 10]

Working — We simply added a negative(-) sign in front of the second attribute. And as a result, we have a list sorted on the basis of descending order of chemistry_score.

Note: Never add a negative sign in front of a function and string as it will give you an error. For example, if you’d try to sort on the basis of name and in descending order using negative(-) sign then you will get the below error.

sorted_result = sorted(students,key = lambda student:-student.name)
[print(student) for student in sorted_result]
#logs error-----------------------------------------
TypeError: bad operand type for unary -: 'str'

Conclusion

To conclude, the sorted() function gives us the ability to manipulate our iterables and sort them very efficiently. And after reading this article, I am sure you will never be troubled with sorting for any data at hand.

That is all for the sorted() function. Happy Coding.

Let the author know what's in your mind !!

This site uses Akismet to reduce spam. Learn how your comment data is processed.