1

I have a large list of tuples that follow the structure below:

('global_access', '2395', 'SQA', 'e69e1c69', '0', '/home/daoxley/git_stuff/sqa/sqa.yaml', '/home/daoxley/git_stuff/sqa/', '1.57.0')
('global_access', '2320', 'SQA', '7d1290cc', '0', '/home/daoxley/git_stuff/sqa/sqa.yaml', '/home/daoxley/git_stuff/sqa/', '1.57.0-7-g7d1290c')
('global_access', '2281', 'SQA', 'ead134e7', '0', '/home/daoxley/git_stuff/sqa/sqa.yaml', '/home/daoxley/git_stuff/sqa/', '1.57.0-9-gead134e')
('global_access', '2230', 'SQA', '8e3404b3', '0', '/home/daoxley/git_stuff/sqa/sqa.yaml', '/home/daoxley/git_stuff/sqa/', '1.57.0-12-g8e3404b')
('global_access', '2158', 'SQA', 'b19ba1fa', '0', '/home/daoxley/git_stuff/sqa/sqa.yaml', '/home/daoxley/git_stuff/sqa/', '1.57.0-15-gb19ba1f')
('global_access', '2345', 'SQA', '03fbe13d', '0', '/home/daoxley/git_stuff/sqa/sqa.yaml', '/home/daoxley/git_stuff/sqa/', '1.58.0')

I am trying to order this data based on the elements inside with some in ascending order and others descending. The order I want is 3rd (asc), 6th (asc), 7th (asc), 8th (desc) and 4th (desc).

I have done most of this already but I do not know how to order the 8th element in descending order so that the data will instead look like this:

('global_access', '2345', 'SQA', '03fbe13d', '0', '/home/daoxley/git_stuff/sqa/sqa.yaml', '/home/daoxley/git_stuff/sqa/', '1.58.0')
('global_access', '2158', 'SQA', 'b19ba1fa', '0', '/home/daoxley/git_stuff/sqa/sqa.yaml', '/home/daoxley/git_stuff/sqa/', '1.57.0-15-gb19ba1f')
('global_access', '2230', 'SQA', '8e3404b3', '0', '/home/daoxley/git_stuff/sqa/sqa.yaml', '/home/daoxley/git_stuff/sqa/', '1.57.0-12-g8e3404b')
('global_access', '2281', 'SQA', 'ead134e7', '0', '/home/daoxley/git_stuff/sqa/sqa.yaml', '/home/daoxley/git_stuff/sqa/', '1.57.0-9-gead134e')
('global_access', '2320', 'SQA', '7d1290cc', '0', '/home/daoxley/git_stuff/sqa/sqa.yaml', '/home/daoxley/git_stuff/sqa/', '1.57.0-7-g7d1290c')
('global_access', '2395', 'SQA', 'e69e1c69', '0', '/home/daoxley/git_stuff/sqa/sqa.yaml', '/home/daoxley/git_stuff/sqa/', '1.57.0')

Currently my code to sort looks like this:

rule_list_to_check.sort(key=lambda x:  (x[2].lower(), x[5], x[6], LooseVersion(x[7])), -int(x[4]))

I have tried using reverse as below but this gives me the following error:

rule_list_to_check.sort(key=lambda x:  (x[2].lower(), x[5], x[6], LooseVersion(x[7]), -int(x[4]),reverse=(False, False, False, True, False ))

And also trying to add - to LooseVersion(x[7]) gives an error also

CDJB
  • 14,043
  • 5
  • 29
  • 55
  • 1
    You haven't posted either of the errors you refer to. – Scott Hunter Jan 07 '20 at 14:33
  • `reverse` is a single Boolean, not a set of Booleans to apply to each "field" (which are just artifacts of how tuples naturally compare, not something `sort` itself knows about). – chepner Jan 07 '20 at 14:37
  • A quick hack is to order it descending for all (3rd, 6th, 7th, 8th, and 4th) and then order it again ascending for only (3rd, 6th, 7th) – 576i Jan 07 '20 at 14:37
  • Does this answer your question? [Sort a list by multiple attributes?](https://stackoverflow.com/questions/4233476/sort-a-list-by-multiple-attributes) – Gary Jan 07 '20 at 15:19

2 Answers2

2

I would define an explicit comparison function, then use functools.cmp_to_key.

from operator import itemgetter

# O compare, how I miss thee
def compare(x, y):
    if x < y:
        return -1
    elif x == y:
        return 0
    else:  # x > y
        return 1


# Comparing modified versions of the elements
def compare_on(x, y, f=None):
    if f is not None:
        x = f(x)
        y = f(y)
    return compare(x, y)


def compare_tuples(t1, t2):
    # Compare the first three fields in ascending order,
    # but the last two fields in descending order
    return (compare_on(t1, t2, itemgetter(2, 5, 6)) or
            compare_on(t2, t1, itemgetter(7, 3))
    # NOTE that t1 and t2 are swapped between the two calls to compare


rule_list_to_check.sort(key=functools.cmp_to_key(compare_tuples))
chepner
  • 497,756
  • 71
  • 530
  • 681
1

Another option is to wrap your tuples in a class that defines the desired total ordering.

from functools import total_ordering
from itertools import itemgetter

b1 = itemgetter(2, 5, 6)
b2 = itemgetter(7, 3)

@total_ordering
class MyTuple(tuple):
    def __lt__(self, other):
         return b1(self) < b1(other) and b2(other) < b2(self)

rule_list_to_check.sort(key=MyTuple)
chepner
  • 497,756
  • 71
  • 530
  • 681