David542
David542

Reputation: 110143

Complex sort in python

I have the following list of tuples:

items = [
    ('john jones', ['Director', 'Screenwriter', 'Producer']), 
    ('eric smith', ['Screenwriter']), 
    ('anne smith', ['Producer']), 
    ('emily smith', ['Director']), 
    ('steven jones', ['Director', 'Screenwriter'])
]

I need to sort it such that "Director" appears before "Screenwriter" appears before "Producer". The actual ordering therein doesn't matter. For example, this would be a valid outcome:

items = [
    ('john jones', ['Director', 'Screenwriter', 'Producer']), 
    ('emily smith', ['Director']), 
    ('steven jones', ['Director', 'Screenwriter'])
    ('anne smith', ['Producer']), 
    ('eric smith', ['Screenwriter']), 
]

Is there a way to do this sort doing sorted(items, key=lambda x: ?), or do I have to iterate each item in the list?

Upvotes: 0

Views: 213

Answers (2)

Igor Chubin
Igor Chubin

Reputation: 64563

Of course, there is a way:

$ cat sort.py
order = ['Director', 'Screenwriter', 'Producer']
items = [
    ('john jones', ['Director', 'Screenwriter', 'Producer']), 
    ('eric smith', ['Screenwriter']), 
    ('anne smith', ['Producer']), 
    ('emily smith', ['Director']), 
    ('steven jones', ['Director', 'Screenwriter'])
]

for i in sorted(items, key = lambda x: order.index(x[1][0])):
    print i

Let's try it:

$ python sort.py
('john jones', ['Director', 'Screenwriter', 'Producer'])
('emily smith', ['Director'])
('steven jones', ['Director', 'Screenwriter'])
('eric smith', ['Screenwriter'])
('anne smith', ['Producer'])

And when you want to sort equal eintries by name, you just need a tuple (Lattyware 's idea):

for i in sorted(items, key = lambda x: (order.index(x[1][0]), x[0])):
    print i

Upvotes: 3

Gareth Latty
Gareth Latty

Reputation: 88977

Here is what I consider the best overall solution, mainly a mashup of other answers here:

import collections

items = collections.OrderedDict([
    ('john jones', ['Director', 'Screenwriter', 'Producer']),
    ('emily smith', ['Director']),
    ('steven jones', ['Director', 'Screenwriter']),
    ('anne smith', ['Producer']),
    ('eric smith', ['Screenwriter']),
])

ordering = {'Director': 1, 'Screenwriter': 2, 'Producer': 3}

def order(item):
    name, values = item
    return ordering.get(values[0], float("inf")), name

print(sorted(items.items(), key=order))

Giving us:

[('emily smith', ['Director']), ('john jones', ['Director', 'Screenwriter', 'Producer']), ('steven jones', ['Director', 'Screenwriter']), ('eric smith', ['Screenwriter']), ('anne smith', ['Producer'])]

And roles not in the ordering dict will be placed at the end. They will be first sorted by role, then by name.

This presumes the sublists are pre-sorted. If not, it is easy to sort them first.

Upvotes: 0

Related Questions