Skip to main content

How to Sort Objects by a Custom Property in Python

Tags

How to Sort Objects by a Custom Property in Python

If you are like me and sometimes you forget the syntax on how to sort a list of custom objects by property then this tutorial is for you.  It seems like there is always a need to sort and manipulate custom objects by property and that was my why I wanted to write this tutorial.  To demonstrate how to take a custom list of objects and sort those objects by a specific property.  In the case of this tutorial I have chosen to use a date property because sorting by date is a very real world example of something you may run into in the work place, but the same code would also work for numbers or strings as well.  By the end of this tutorial you should know how to sort a custom list of objects by any specific property of that object.

NOTE: This tutorial was tested with Python 2 and 3 on a macOS and Linux operating system.  Windows was not tested, but running this tutorial in a Windows subsystem should produce the same results.  Please let me know if it does not.  👍

 

Setup the Basics 👨‍💻

Below what I want to do is set the stage for a real world situation that you may encounter when working with Python.  I created a very basic custom object to represent articles found on this blog.  This custom object contains a title and a date property.  Below the custom object in the main function I created five custom objects each with a date and a title in random order.  For display purposes I then print out each of  the objects to show that it is not in any sort of order.

# Custom object to hold article date and time
class customObject():
 
	def __init__(self, title, date):
		self.title = title
		self.date = datetime.strptime(date, "%B %d, %Y")
 
 
def main():
 
	obj3 = customObject("TLS 1.3 - Better, Stronger, Faster", "January 6, 2018")
	obj4 = customObject("User Interface Testing with Swift and XCTest", "December 10, 2017")
	obj2 = customObject("How to Use Python List Comprehensions", "December 2, 2017")
	obj1 = customObject("Attending WWDC 2017 - Predictions Answered", "June 13, 2017")
	obj5 = customObject("Swift Network Testing - Automate XCTest with Python", "November 26, 2017")
 
	print("---------------------------------------------------------------")
 
	# Display the dates and titles
	print("Unsorted Date from obj1: " +str(obj1.date) + " with title: " +obj1.title)
	print("Unsorted Date from obj2: " +str(obj2.date) + " with title: " +obj2.title)
	print("Unsorted Date from obj3: " +str(obj3.date) + " with title: " +obj3.title)
	print("Unsorted Date from obj4: " +str(obj4.date) + " with title: " +obj4.title)
	print("Unsorted Date from obj5: " +str(obj5.date) + " with title: " +obj5.title +"\n")

 

Sorting the Objects 🎉

Now for the fun part.  In the real world you will probably encounter a situation where you will need to sort a data structure or a list by property.  So I took my unsorted objects and added them to a list - out of order.  From here I used the Python sort method to sort my list by date property in descending order using the optional key and reverse parameters.  From here I used a for loop and printed out the newly sorted list to display the successful sort of my list.

So how does this apply to sorting custom objects by property?  Well, the sort method I used in this example can be used to sort any collection of like objects by property.  For example, if you take a look at the key parameter you will see that I used an inline lambda function as the argument and specified the date on customObject, but this could have just as easily been the title property too.   The key parameter is used to identify the items to sort the objects on.   Next you will see that I input a true argument to the reverse parameter.  This is to let the sort method know that I want to sort these objects descending.  And that is all there is to it.  These technique could be used with numeric or string values also, not just dates.  It is highly flexibly and very valuable to incorporate in your development toolbox.

customObjects = [obj1, obj2, obj3, obj4, obj5]
 
# One line sort function method using an inline lambda function lambda x: x.date
# The value for the key param needs to be a value that identifies the sorting property on the object
customObjects.sort(key=lambda x: x.date, reverse=True)
 
for obj in customObjects:
	print("Sorted Date from obj: " +str(obj.date) + " with title: " +obj.title)

In Summary ⌛️

Now you know more about how to sort objects by custom property in Python!  Please feel free to download the code example from my Github and mess around with it.  Please let me know if you have any questions, comments, or concerns and please feel free to take a look at other tutorials on this website.

Thank you very much for reading!

Credits: Cover image designed by Freepik.

Member for

3 years 9 months
Matt Eaton

Long time mobile team lead with a love for network engineering, security, IoT, oss, writing, wireless, and mobile.  Avid runner and determined health nut living in the greater Chicagoland area.

Comments

parsme

Tue, 01/15/2019 - 09:56 PM

Thanks, that was exactly what I was looking for. You're so good, thanks really.

Thank your for the article. It works perfectly, but I'm sad to say that even reading your article and your link about the inline lambda function I just do not understand how or why it works.

I guess my problem is I don't understand what the lambda function does that makes the sort() function understand what key you want to sort by. Or rather why this works, but writing key=date or key=x.date does not work.

Do you have a hint or a website where I could perhaps learn to understand this better?

Regardless thank you.

Anvil,

Thank you very much for your feedback.  I appreciate it.

Let me expand on the sort() method in Python a bit.  It is based off of the C sort() / qsort() standard library method.  The x: x.date argument is the key in the object that the collection (list) of objects will be sorted upon.  This is the value each object is evaluated at and positioned in the new collection by.  Think of the key being the value that sorts the object on the x-axis.  The x-axis being right to left; with a reverse of true or false, meaning the sort order of the x-axis.

Let me know if you're looking for more information and I can certainly provide it!

I think I understand all that. As in, I understand what it does (makes sort() sort the list according to the key. But I don't understand how or why it makes it all work.

Why does this work?
customObjects.sort(key=lambda x: x.date, reverse=True)

While none of these do?
customObjects.sort(key=x.date, reverse=True)
customObjects.sort(key=date, reverse=True)

I'm very new to Python (and programming) and working on an exam project for it. This is really just a side issue and not important for the project as such. But ideally I would like to understand the why of it rather than just what the result ends up being, if that makes sense?

Hope I didn't misread your explanation :)

Anvil, this is a great question.  It looks like the reason your code may not be working is that the key argument is not presented as a function. 

The key=lambda x: x.date represents an creates anonymous function that is passed into sort().  Check out the documentation for list.sort(), it say that the key argument specifies a function of one argument that is used to extract a comparison key from each list element.  Specifying just the x.date in this case would not create a function argument.

Here are a few links to where Python references this information and how it's executed in C under the hood:

  1. https://docs.python.org/3.3/library/stdtypes.html#list.sort
  2. https://github.com/python/cpython/blob/234531b4462b20d668762bd78406fd2ebab129c9/Objects/listobject.c#L2208

 

Let me know if that helps at all?

 

Yes! Thank you so much for your help.

A lot of the technical stuff escapes me (reading the C implementation is definitely beyond me at this point), but I managed to make some code of my own, that works. Granted, yours worked for me as well, but I feel like I have a better grasp on it now.

If you're curious, I wanted to sort a list of materials according ot their ID number and what I ended up with was the following:
def sort_material_by_id(material):
return materiale.idnr

list_materials.sort(key=sort_material_by_id)

Anvil,

Great to hear that this helped you!  Yes, your code should absolutely work for that use case.  The code you posted is actually a great example not described in my post either.  Thank's for following up!

Paul_e

Sat, 09/28/2019 - 06:12 PM

Hello everyone,

I revive this topic since I really hope and beg you guys to help me understand something please. I am kind a new in python and especially OOP.

I have a class and this class stores all the objects instantiated in a list (declared as class variable). Well, I would like to sort this list by an attribute (all objects have this attribute since is declared in __init__ method).

Please can you help me with an advice on the below example?

class Example:
list1 = []
def __init__(self, attrbute1, attrbute2):
self.attrbute1 = attribute1
self.attrbute2 = attribute2
Example.list1.append(self)
def sort_list1(self):
Example.list1.sort(key=lambda x: x.attribute1, reverse=False)

Then outside the class
I create objects( obj = Example("abc", "123"), etc) and print(Example.list1)
I receive a list full with memory locations of objects and neither the sort is performed.

I am very very grateful for any advice.

Excellent question Paul E! In this situation I would recommend removing the list from the actual object you are trying to sort.

For example, if you want to sort custom objects, I would hold the list in memory outside of the class, instantiate the class and then add the newly created object to a list that is stored outside of the context of the object.  This will provide better results.

class A:

def __init__(self, att1):

    self.att1 = att1

 

class B:

list1 = []

def buildObject(self):

    obj1 = A("abc")

    obj2 = A("def")

    self.list1.append(obj1)

    self.list1.append(obj2)

def sort_list1(self):

    self.list1.sort(key=lambda x: x.att1, reverse=False)

Thank you very much Matt! This works very well indeed!

I was thinking the list of objects to hold all the objects created and just sort through all of them at one point.
Do you think is possible to get a similar outcome by having that list of objects declared as class variable?

I don't got that much experience at this point (especially with OOP) and I am not 100% sure it will work. I couldn't do it like that.

Again thank you very much for the assist. This is more than helpful!

Paul_e.  No problem, always glad to help.

In regards to the class variable, the current list1 is setup as a class variable for class B.  For example, self.list1.append(obj1).

I think this will work well as a class variable for any object other than A (or the object being added to the list).

Hope this helps.

Anonymous

Thu, 02/13/2020 - 10:08 AM

What if a object has few names and few numbers and I want to sort according to numbers and if numbers are equal than sort the alphabets

Mike B.

Sat, 07/24/2021 - 02:59 PM

This is perfect and just what I was looking for. Also, thank you for the detailed and clear explanation of how it works.