1

I have the following list of lists of "category" objects in a variable called "category_tree", which is a list:

[[<Category: State>, <Category: County>, <Category: Chicago>], 
[<Category: Animal>],
[<Category: State>, <Category: County>], 
[<Category: State>, <Category: County>, <Category: NYC>], 
[<Category: Animal>, <Category: Fish>], 
[<Category: State>, <Category: County>, <Category: LA>], 
[<Category: Animal>, <Category: Frog>, <Category: TreeFrog>, <Category: Fred>]]

I need to sort the top-level list by the .name properties of the underlying lists. I do not want to sort the underlying lists, just the top-level list.

Assuming the .name elements match what is listed here, this is what I'm trying to get:

[[<Category: Animal>], 
[<Category: Animal>, <Category: Fish>], 
[<Category: Animal>, <Category: Frog>, <Category: TreeFrog>, <Category: Fred>]
[<Category: State>, <Category: County>], 
[<Category: State>, <Category: County>, <Category: Chicago>],
[<Category: State>, <Category: County>, <Category: LA>], 
[<Category: State>, <Category: County>, <Category: NYC>]]

I can sort them by the first or the last column with:

category_tree.sort(key=lambda x: x[0].name)
category_tree.sort(key=lambda x: x[-1].name)

But the problem I'm running into is that they have variable numbers of columns, so I can't sort them this way for all internal columns.

What is the best way to do this?

Adam
  • 2,820
  • 1
  • 13
  • 33
SHq88
  • 37
  • 5
  • What are you trying to achieve with this category tree? Seems more logical to use a dictionary for these type of things. – Miller-STGT May 27 '20 at 14:17
  • As far as what I'm trying to achieve -- I'm dealing with outputs from a django website back-end that I get in this form, and I can't change that part of it. I could convert them to some other form and then back to this, if needed. In the end, I just need to sort them. – SHq88 May 27 '20 at 14:24

1 Answers1

3

A slighty modified example, with a possible solution:

class Category:
    def __init__(self, name):
        self.name = name

    def __repr__(self):
        return self.name

items = [
    [Category('State'), Category('County'), Category('Chicago')],
    [Category('Animal')],
    [Category('State'), Category('County')],
    [Category('Animal'), Category('Frog')],
    [Category('Animal'), Category('Fish'), Category('Dog')],
]

items.sort(key=lambda x: [item.name for item in x])
print(items)

results in

[[Animal], [Animal, Fish, Dog], [Animal, Frog], [State, County], [State, County, Chicago]]

This relies on the ability of Python to compare lists directly, provided the individual items in the list can be compared properly. For integers, floats and strings, that is not a problem. For a (basic) class, it is, since it not defined by default.

If you have access to the Category class (i.e., you wrote it yourself), you can override __eq__ and other magic comparison methods, which makes things a lot simpler (see e.g. Compare object instances for equality by their attributes ).

With help from the functools module:

from functools import total_ordering

@total_ordering 
class Category:
    def __init__(self, name):
        self.name = name

    def __eq__(self, other):
        return self.name == other.name

    def __lt__(self, other):
        return self.name < other.name

    def __repr__(self):
        return self.name

And then simply:

items.sort()

(items as before).

With the same result: [[Animal], [Animal, Fish, Dog], [Animal, Frog], [State, County], [State, County, Chicago]].

9769953
  • 10,344
  • 3
  • 26
  • 37