8

I was looking a lot and reading a lot of question, but I cannot figure out how to give two arguments to the key of sort method, so I can make a more complex comparison.

Example:

class FruitBox():
  def __init__(self, weigth, fruit_val):
    self.weigth = weigth
    self.fruit_val = fruit_val

I want to compare the FruitBox by fruit_val, but! Also they box heavier are bigger than others.

So it would be:

f1 = FruitBox(2,5)
f2 = FruitBox(1,5)
f3 = FruitBox(2,4)
f4 = FruitBox(3,4)

boxes = [f1,f2,f3,f4]
boxes.sort(key = ???) # here is the question

Expected result: => [FruitBox(2,4),FruitBox(3,4),FruitBox(1,5),FruitBox(2,5)]

Is there a way to send a function with 2 arguments, when I do it something like

def sorted_by(a,b):
  #logic here, I don't know what will be yet

and I do

boxes.sort(key=sorted_by)

It throws:

Traceback (most recent call last):
  File "python", line 15, in <module>
TypeError: sort_by_b() missing 1 required positional argument: 'b'

How can I give Two Arguments to the key of sort?

developer_hatch
  • 15,898
  • 3
  • 42
  • 75
  • For the actual question you're having refer to [python - Sort a list by multiple attributes? - Stack Overflow](https://stackoverflow.com/questions/4233476/sort-a-list-by-multiple-attributes). For the question in title, see [the answer below](https://stackoverflow.com/a/46851576/5267751). – user202729 Aug 14 '21 at 13:33

4 Answers4

19

This answer is dedicated to answering:

How can I give Two Arguments to the key of sort?


The old style compare way to sort is gone in Python 3, as in Python 2 you would do:

def sorted_by(a,b):
    # logic here
    pass

boxes.sort(cmp=sorted_by)

But if you must use it Python 3, it’s still there, but in a module, functools, it’s purpose is to convert the cmp to key:

import functools 
cmp = functools.cmp_to_key(sorted_by)
boxes.sort(key=cmp)

The preferred way to sort is to make a key function that returns a weight for the sorting to base on. See Francisco’s answer.

user202729
  • 3,358
  • 3
  • 25
  • 36
Taku
  • 31,927
  • 11
  • 74
  • 85
12

If you want to sort using two keys, you can do it like this (I suppose you want to sort first by fruit_val then by weight:

boxes.sort(key=lambda x: (x.fruit_val, x.weigth))
FcoRodr
  • 1,583
  • 7
  • 15
5

The docs, section on Odd and Ends says:

The sort routines are guaranteed to use __lt__() when making comparisons between two objects. So, it is easy to add a standard sort order to a class by defining an __lt__() method.

In your example that translates to adding the __lt__() to your FruitBox class:

class FruitBox():
    def __init__(self, weigth, fruit_val):
        self.weigth = weigth
        self.fruit_val = fruit_val

    def __lt__(self, other):
        # your arbitrarily complex comparison here:
        if self.fruit_val == other.fruit_val:
             return self.weight < other.weight
        else:
             return self.fruit_val < other.fruit_val

        # or, as simple as:
        return (self.fruit_val, self.weight) < (other.fruit_val, other.weight)

Then use it simply like this:

sorted(fruitbox_objects)
randomir
  • 17,989
  • 1
  • 40
  • 55
0

You can sort using the key parameter with the fruit_val member variable:

boxes = [f1,f2,f3,f4]
boxes.sort(key=lambda x:x.fruit_val)
print([i.__dict__ for i in boxes])

Output:

[{'fruit_val': 4, 'weigth': 2}, {'fruit_val': 4, 'weigth': 3}, {'fruit_val': 5, 'weigth': 2}, {'fruit_val': 5, 'weigth': 1}]
Ajax1234
  • 69,937
  • 8
  • 61
  • 102