简体   繁体   中英

Common elements between two lists and preserving the order of elements in the two lists

I have two lists list1 and list2 . I've found on stackoverflow a very simple method to get the common elements in this two lists as follows result = list(set(list1) & set(list2)) . Unfortunately, with this, the order of elements in the resulting list, is not preserved.

For instance:

list1 = ['a', 'e', 't', 'b', 'c']
list2 = ['e', 'b', 'a', 'c', 'n', 's']

I want the result (common elements) to be ['e', 'a', 'b', 'c'] in this order. Because, for instance, 'e' is in list1 and in list2 and is in position 2 in list1 and position 1 in list2, while 'a' is in list1 and in list2 and is in position 1 in list1 and position 3 in list2, so 'e' is before 'a' since 2+1 < 1+3.

So, is there any simple way to have the common elements between two lists and preserving the order of elements ?

list1 = ['a', 'e', 't', 'b', 'c']
list2 = ['e', 'b', 'a', 'c', 'n', 's']

weights = defaultdict(int)

for i, e in enumerate(list1):
   weights[e] += i

for i, e in enumerate(list2):
   weights[e] += i

>>> result = sorted(set(list1) & set(list2), key=lambda i: weights[i])
>>> result
['e', 'a', 'b', 'c']

You could use a list comprehension to filter all elements from list1 that don't also belong to list2 :

list1 = ['a', 'e', 't', 'b', 'c']
list2 = ['e', 'b', 'a', 'c', 'n', 's']

result = [item for item in list1 if item in list2]
print result

Result:

['a', 'e', 'b', 'c']

Although this does not conform to the desired result in your main post, from your follow-up comment it seems like this is an acceptable result.


You could also continue using the set approach, and then sort the results after the fact using the positioning algorithm you described:

list1 = ['a', 'e', 't', 'b', 'c']
list2 = ['e', 'b', 'a', 'c', 'n', 's']

items = set(list1) & set(list2)
result = sorted(items, key=lambda element: list1.index(element) + list2.index(element))

print result

Result:

['e', 'a', 'b', 'c']

You can preserve the order of one list like this, by making only one of them a set:

list1 = ['a', 'e', 't', 'b', 'c']
list2 = ['e', 'b', 'a', 'c', 'n', 's'] 

slist1 = set(list1)
slist2 = set(list2)

# list1 determines the ordering
olist1 = [x for x in list1 if x in slist2]

# list2 determines the ordering
olist2 = [x for x in list2 if x in slist1]

Basically, you iterate over one list, and check each item to see if it's in the other. This ensures that your final list is in the same order as the original, but also that it only includes entries in both lists.

Using sets speeds up the inclusion checking considerably.

If you want to do anything more complex than this (make final ordering dependent on indexes in both lists, for instance), then you'd need to be more specific on those details.

edit: OK, you did post more details about what you want to do. Don't know if it's the fastest , but you can do something like this to get the indices:

list1_offsets = dict(x[::-1] for x in enumerate(list1))
list2_offsets = dict(x[::-1] for x in enumerate(list2))

total_list = slist1 & slist2

total_offset = [(x, list1_offsets[x] + list2_offsets[x]) for x in total_list]

final_list = [x[0] for x in sorted(total_offset, key=itemgetter(1))]

In this case, the output was the expected ['e', 'a', 'b', 'c'] .

Your ordering function seems pretty odd but ok. You need to find the intersection and sort it by your ordering function basically.

order_function = lambda val: list1.index(val) + list2.index(val)
common = set(list1) & set(list2)
indexed = [(order_function(v), v) for v in common]
return [x[1] for x in sorted(indexed)]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM