0

I have a list of objects instantiated from a class. I need to sort the list using 'x' and 'is_start' parameters.

I tried using the total_ordering module from functools and custom wrote the lt & eq methods.

Class:

@total_ordering
class BuildingPoint(object):
    def __init__(self):
        self.x = None
        self.height = None
        self.is_start = None

    def __lt__(self, other):
        if self.x != other.x:
            return self.x < other.x

    def __eq__(self, other):
        if self.x == other.x:
            # If both points are starting points then building with higher height
            # comes earlier
            if self.is_start and other.is_start:
                return self.height > other.height
            # If both points are ending points then building with lower height
            # comes earlier            
            if not self.is_start and not other.is_start:
                return self.height < other.height

Now if I want to sort this list of BuildingPoint objects where the first and third objects have same x and is_start:

building_points = [[0, 2, True], [1, 2, False], [0, 3, True], [2, 3, False]]

Sorting building_points should give this output:

sorted(building_points)
>>[[0, 3, True], [0, 2, True], [1, 2, False], [2, 3, False]]

But it's returning the same object list. Any advice on how to do this?

Adda
  • 23
  • 3

1 Answers1

1

As @juanpa.arrivillaga mentioned, your __lt__ and __eq__ were broken. I just fixed __lt__ and removed __eq__, I think that's what you intended to do. Also, you're sorting a list of arrays, not your BuildingPoint object. I fixed your __init__ to create a BuildingPoint from an array. And finally, I added a __repr__ method to be able to display the object.

I'm not sure if that's you want to do, here is what I did:

from functools import total_ordering

@total_ordering
class BuildingPoint(object):
    def __init__(self,x,h,start):
        self.x = x
        self.height = h
        self.is_start = start

    def __repr__(self):
        return "[{},{},{}]".format(self.x,self.height,self.is_start)

    def __lt__(self, other):
        if self.x != other.x:
            return self.x < other.x
        else:
            if self.is_start and other.is_start:
                return self.height > other.height
            else:
                return self.height < other.height

building_points = [ BuildingPoint(*array) for array in [[0, 2, True], [1, 2, False], [0, 3, True], [2, 3, False]]]
sorted(building_points)

Output:

[[0,3,True], [0,2,True], [1,2,False], [2,3,False]]
Nakor
  • 1,484
  • 2
  • 13
  • 23
  • Hi, I was showing the list of list to give an example of the list of objects. Thanks for adding the code to create the list of objects. You exactly know what I am trying to do. The __lt__ code still returns the original list of objects however. Edit: I was using sorted(building_points), so the original building_points wasn't changed. I should have used building_points.sort(). – Adda Jun 24 '19 at 02:07
  • Oh ok. And what do you mean by `__lt__` returns the original list of objects ? When I do `sorted`, the order is not the same (it's the solution you gave) – Nakor Jun 24 '19 at 02:10
  • Your solution works. I was using sorted(building_points), but I wasn't assigning it to another variable, so my original list was unaffected :) – Adda Jun 24 '19 at 02:13