Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sorting tuples with a custom sorting function using a range?

I would like to sort a list of tuples based on the two last columns:

mylist = [(33, 36, 84), 
          (34, 37, 656), 
          (23, 38, 42)]

I know I can do this like:

final = sorted(mylist, key:lambda x: [ x[1], x[2]])

Now my problem is that I want to compare the second column of my list with a special condition: if the difference between two numbers is less than an offset they should be taken as equal ( 36 == 37 == 38) and the third column should be used to sort the list. The end result I wish to see is:

mylist = [(23, 38, 42)
          (33, 36, 84), 
          (34, 37, 656)]

I was thinking of creating my own integer type and overriding the equal operator. Is this possible? is it overkill? Is there a better way to solve this problem?

like image 817
Cocomico Avatar asked Jun 23 '26 14:06

Cocomico


2 Answers

I think the easiest way is to create a new class that compares like you want it to:

mylist = [(33, 36, 84), 
          (34, 37, 656), 
          (23, 38, 42)]

offset = 2

class Comp(object):
    def __init__(self, tup):
        self.tup = tup

    def __lt__(self, other):  # sorted works even if only __lt__ is implemented.
        # If the difference is less or equal the offset of the second item compare the third
        if abs(self.tup[1] - other.tup[1]) <= offset:
            return self.tup[2] < other.tup[2]
        # otherwise compare them as usual
        else:
            return (self.tup[1], self.tup[2]) < (other.tup[1], other.tup[2])

A sample run shows your expected result:

>>> sorted(mylist, key=Comp)
[(23, 38, 42), (33, 36, 84), (34, 37, 656)]

I think it's a bit cleaner than using functools.cmp_to_key but that's a matter of personal preference.

like image 159
MSeifert Avatar answered Jun 26 '26 03:06

MSeifert


Sometimes an old-style sort based on a cmp function is easier than doing one based on a key. So -- write a cmp function and then use functools.cmp_to_key to convert it to a key:

import functools

def compare(s,t,offset):
    _,y,z = s
    _,u,v = t
    if abs(y-u) > offset: #use 2nd component
        if y < u:
            return -1
        else:
            return 1
    else: #use 3rd component
        if z < v:
            return -1
        elif z == v:
            return 0
        else:
            return 1

mylist = [(33, 36, 84), 
          (34, 37, 656), 
          (23, 38, 42)]

mylist.sort(key = functools.cmp_to_key(lambda s,t: compare(s,t,2)))

for t in mylist: print(t)

output:

(23, 38, 42)
(33, 36, 84)
(34, 37, 656)
like image 32
John Coleman Avatar answered Jun 26 '26 05:06

John Coleman



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!