0

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?

1
  • A collections.OrderedDict might be an appropriate structure here. Commented Jun 15, 2012 at 18:48

2 Answers 2

3

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
Sign up to request clarification or add additional context in comments.

6 Comments

This works, and was my initial thought, but I actually prefer the dictionary approach that Joel Cornett's answer uses - it's more explicit and clear. It might be worth making it go to a tuple of ordering, name so that it sorts on name after ordering, rather than being in the order they were.
@Lattyware: That is not a problem at all. You just need to specify lambda x: correct_ordering.get(x[1][0], 999) instead of index. By the way "dictionary approach" is better because you need not to specify all "levels". That that are not specified will be at the end of the sorted list.
Was that comment in reply to me? I don't really see how it applies to what I said.
@Lattyware: Why do you not see it? That is a "dictionary approach". Did you mean something else?
Sorry, reading it again I see what you meant - it was just that you offered improvements while talking about it, it threw me. Note that float("inf") to get infinity might be better than an arbitrary 999.
|
0

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.

2 Comments

I still feel like the use of OrderedDict is assuming the OP manually populates the source dict and not just gets it from some other return value.
@jdi That's a valid point, but it's pretty easy to change this not to use one.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.