0

I have 2 lists, for example:

a = ['a','b','c','d','e']
b = ['c','a','dog']

I would like to sort the common elements in list b by the order of list a, to get something like this:

['a','c','dog']

I have read similar questions using sorted(), however I cannot make it work when the lists do not contain the same elements (i.e. 'dog' in list b).

4
  • Can't you just filter list a using b and then sort it? Commented May 30, 2013 at 16:53
  • Can you provide an example where the sorted list is not simply sorted by lexicographical order? Because it’s not really obvious what role a plays when it’s just sorted naturally like that. Commented May 30, 2013 at 16:53
  • It's not clear what behavior you want it to have when 'dog' isn't in list a. Commented May 30, 2013 at 16:54
  • What do you want to get if it's ['c', 'eagle', 'a','dog']? Commented May 30, 2013 at 16:54

4 Answers 4

4
>>> a = ['a','b','c','d','e']
>>> b = ['c','a','dog']
>>> def func(x):
...     try:
...         return a.index(x)
...     except ValueError:
...         return float("inf")
...     
>>> sorted(b, key = func)
['a', 'c', 'dog']
Sign up to request clarification or add additional context in comments.

Comments

2

I'd turn a into dictionary:

a_dict = dict((v, i) for i, v in enumerate(a))

and use float('inf') to indicate values to be sorted at the end:

sorted(b, key=lambda v: a_dict.get(v, float('inf')))

Demo:

>>> a = ['a','b','c','d','e']
>>> b = ['c','a','dog']
>>> a_dict = dict((v, i) for i, v in enumerate(a))
>>> sorted(b, key=lambda v: a_dict.get(v, float('inf')))
['a', 'c', 'dog']

This has the advantage of speed; dict lookups are O(1) versus list .index() lookups having a O(n) cost. You'll notice this more as a and b grow in size.

The disadvantage is that duplicate values in a are handled differently; the dict approach picks the last index versus .index() picking the first.

2 Comments

That disadvantage isn't really much of one as the list can be processed once (no big shakes as one is saving on the lookup) by using dict( (v, i) for i, v in enumerate(OrderedDict.fromkeys(a)) ) instead...
I was thinking using reversed() could solve the 'problem' here, actually. :-) But an OrderedDict could well be faster in that case, as reversing enumerate() would require materializing the enumeration in memory..
0

One option is to use bisect

import bisect
from operator import itemgetter
a = ['a','b','c','d','e']
b = ['c','a','dog']
l = sorted([(x, bisect.bisect(a, x)) for x in b], key=itemgetter(1))
l = [x[0] for x in l]
print l
['a', 'c', 'dog']

Comments

0

You could use (frozen) sets. I have not timed this against other answers.

>>> a = ['a','b','c','d','e']
>>> b = ['c','a','dog']
>>> list((frozenset(a)^frozenset(b))^frozenset(a))
['a', 'c', 'dog']

1 Comment

Python sets are unordered collections. This works when I test it but will this solution work all the time?

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.