-2

I'm having an issue when sorting a list of custom class objects in Python based on one of their properties in Python. I have a list of Product objects (made this class myself as seen below), each with attributes name, price, and rating. I'm trying to sort the list by the price property in ascending order.

Here's the code I have with key=lambda x: x.price which I found from another question

class Product:
    def __init__(self,name,price,rating):
        self.name=name
        self.price=price
        self.rating=rating

product_list = [
Product("Laptop", 900, 4.5),
Product("Headphones", 100, 4.2),
Product("Keyboard", 50, 4.8),
Product("Monitor", 350, 4.6)
]

sorted(product_list, key=lambda x: x.price)
for product in product_list:
    print(product.name, product.price, product.rating)

However, this code doesn't seem to sort the list - it's still in the original order. And the weird thing is I'm not receiving any errors, so I can't find out where the mistake is.

Could someone please help me understand what's happening? Am I using the sorted function incorrectly in this context with the lambda thing? Sorry if this is a noob question.

5 Answers5

0

The issue is that sorted() is not an in-place sort, so you need to assign the result to a variable. sorted() returns a new list, so you can't just do sorted(product_list, key=lambda x: x.price) and expect product_list to be sorted.

Luckily, it's an easy fix:

product_list = sorted(product_list, key=lambda x: x.price)
for product in product_list:
    print(product.name, product.price, product.rating)
juanpethes
  • 831
  • 2
  • 18
  • 1
    Thank you for all the replies, it works! sorry for the oversight of me –  Aug 07 '23 at 15:14
0

sorted() returns a value; it does not sort your list. So that line can be

sorted(product_list, key=lambda x: x.price)

or use the sort() function to sort in-place.

product_list.sort( key=lambda x: x.price)

Joshua Fox
  • 18,704
  • 23
  • 87
  • 147
0

Assign the output to a variable,

sorted_objects = sorted(product_list, key=lambda x: x.price)

Updated your class:

class Product:
    def __init__(self,name,price,rating):
        self.name=name
        self.price=price
        self.rating=rating
    
    def __repr__(self):
        class_name = self.__class__.__name__
        return f'{class_name}(name={self.name}, price={self.price}, rating={self.rating})'

Output:

>>> print(sorted_objects)

[Product(name=Keyboard, price=50, rating=4.8),
 Product(name=Headphones, price=100, rating=4.2),
 Product(name=Monitor, price=350, rating=4.6),
 Product(name=Laptop, price=900, rating=4.5)]
shaik moeed
  • 5,300
  • 1
  • 18
  • 54
  • Thank you for all the replies, it works! sorry for the oversight of me –  Aug 07 '23 at 15:14
  • @johnsmith I have updated your class, to see the sorted list in a meaningful manner. Don't forget to accept one of the answers that most helped to consider your question was answered :) – shaik moeed Aug 07 '23 at 15:15
  • Thank you so much. I try to accept but it says I have to wait –  Aug 07 '23 at 15:18
  • Also I don't understand what you mean a meaningful manner, the old way also worked...? –  Aug 07 '23 at 15:24
  • 1
    @johnsmith I have added [`__repr__`](https://stackoverflow.com/questions/1984162/purpose-of-repr-method) method to your class so that when you see the sorted array, you can have a better understanding and meaningful representation. – shaik moeed Aug 07 '23 at 15:32
  • 1
    @johnsmith By using this, you can avoid using for loop, by default it will show you the proper class definition. – shaik moeed Aug 07 '23 at 15:38
  • Oh it makes more sense now so I just do `print(product_list)` after sorting, but how did you get it to print with newlines? I get it in one long line like this: `[Product(name=Keyboard, price=50, rating=4.8), Product(name=Headphones, price=100, rating=4.2), Product(name=Monitor, price=350, rating=4.6), Product(name=Laptop, price=900, rating=4.5)]` –  Aug 07 '23 at 15:41
  • 1
    @johnsmith You can use [pretty](https://docs.python.org/3/library/pprint.html) printer like `from pprint import pprint`, now use `pprint(product_list)` instead of `print(product_list)` – shaik moeed Aug 07 '23 at 15:43
0

sorted doest not sort in-place, use sort() instead or you just have to assign your sorted list back to your product_list

   product_list.sort(key=lambda x: x.price) # sorts in-place
   product_list = sorted(product_list, key=lambda x: x.price)# assign back to list
nisakova
  • 89
  • 6
  • Which one would you recommend or it doesn't matter? –  Aug 07 '23 at 15:29
  • @johnsmith It depends entirely on your needs. If you don't need the list in its original order then sort it in place i.e., product_list.sort(). That will also save on memory. If you need both the original order and the sorted order then use *sorted()* – DarkKnight Aug 07 '23 at 16:02
0

Also worth mentioning that if you only ever want to sort by price then you can override the __lt__ dunder method in your class and not have to specify a key to the sort function (whichever variant you decide to use).

For example:

class Product:
    def __init__(self, name, price, rating):
        self.name = name
        self.price = price
        self.rating = rating
    def __lt__(self, other):
        if isinstance(other, type(self)):
            return self.price < other.price
        return False
    def __str__(self):
        return f'Name={self.name}, Price={self.price}, Rating={self.rating}'

product_list = [
    Product("Laptop", 900, 4.5),
    Product("Headphones", 100, 4.2),
    Product("Keyboard", 50, 4.8),
    Product("Monitor", 350, 4.6)
]

print(*sorted(product_list), sep='\n')

Output:

Name=Keyboard, Price=50.00, Rating=4.8
Name=Headphones, Price=100.00, Rating=4.2
Name=Monitor, Price=350.00, Rating=4.6
Name=Laptop, Price=900.00, Rating=4.5
DarkKnight
  • 19,739
  • 3
  • 6
  • 22
  • Thanks for this answer, I learned some more advanced techniques, wish I could upvote answers –  Aug 07 '23 at 17:10