elwc
elwc

Reputation: 1287

How to sort nested list with different priority of characters in a single element in Python?

Sorry for not being able to come out with a good title.

I have a list:

some_list = [[X, '4x01'], [X, '3x02'], [X, '4x02'], [X, '3x01']]

where X is something I don't care.

I want to sort the second element in the list with the following priorities:

  1. The first character in descending order.
  2. The last 2 characters in ascending order.

The final output should be:

some_list = [[X, '4x01'], [X, '4x02'], [X, '3x01'], [X, '3x02']]

Note: The solution that gives the incorrect answer is:

output = sorted(some_list, key=lambda lis: lis[3], reverse=True)

Upvotes: 2

Views: 2607

Answers (2)

NPE
NPE

Reputation: 500595

In this case you could use a composite key as @Amber did. However, that approach doesn't always work: for example, it would fail if both components were two characters long (since the -ord(x[1][0]) trick would no longer work).

Here is a more general solution:

In [15]: X = None

In [16]: some_list = [[X, '4x01'], [X, '3x02'], [X, '4x02'], [X, '3x01']]

In [17]: l = sorted(some_list, key=lambda (x,y):y[-2:])

In [18]: l = sorted(l, key=lambda (x,y):y[:1], reverse=True)

In [19]: l
Out[19]: [[None, '4x01'], [None, '4x02'], [None, '3x01'], [None, '3x02']]

It sorts the list one criterion at a time, and makes use of the fact that Python's sort is stable.

An even more general approach is to provide a comparator:

def f((x1,y1), (x2,y2)):
    c = cmp(y1[:1], y2[:1])
    if c != 0: return -c
    return cmp(y1[-2:], y2[-2:])

This can be used as follows:

In [39]: sorted(some_list, cmp=f)
Out[39]: [[None, '4x01'], [None, '4x02'], [None, '3x01'], [None, '3x02']]

Upvotes: 1

Amber
Amber

Reputation: 526923

output = sorted(some_list, key=lambda x: (-ord(x[1][0]), x[1][-2:]))

Example run (defining X as None just so I can copy-paste your list definition):

>>> X = None
>>> some_list = [[X, '4x01'], [X, '3x02'], [X, '4x02'], [X, '3x01']]
>>> output = sorted(some_list, key=lambda x: (-ord(x[1][0]), x[1][-2:]))
>>> output
[[None, '4x01'], [None, '4x02'], [None, '3x01'], [None, '3x02']]

Upvotes: 6

Related Questions