Identify distinct elements in a list and map it to a corresponding index in another list in python

Viewed 502

I have 2 different lists:

l1 = ['a','b','a','e','b','c','a','d']
l2 = ['t1','t2','t3','t4','t5','t6','t7','t8']

The lengths of l1 and l2 will always be the same. They're in fact logical mappings - each item in l1 corresponds to a value in l2.

I wanted to identify distinct elements in l1. I did that using set and list comprehension as follows:

used = set()
distl1 = [x for x in l1 if x not in used and (used.add(x) or True)]

Here, the output will be:

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

which is nothing but the first occurrence of every distinct element.

Now, how do I build a list distl2 so that I get the output as the value in l2 that corresponds to the first occurrence's value i.e., distl1?

distl2 = ['t1','t2','t4','t6','t8']
4 Answers

My idea is to use an OrderedDict to build a mapping of (key, value) pairs corresponding to the elements of l1 and l2 and then extract the values from that dict as a list.

>>> from collections import OrderedDict
>>> 
>>> l1 = ['a','b','a','e','d','c','a','b']
>>> l2 = ['t1','t2','t3','t4','t5','t6','t7','t8']
>>> 
>>> d = OrderedDict()
>>> for k, v in zip(l1, l2):
...:    if k not in d: # <--- check if this key has already been seen!
...:        d[k] = v
...:        
>>> distl2 = list(d.values())
>>> distl2
>>> ['t1', 't2', 't4', 't5', 't6']

Note for Python 3.7+ users: regular dicts are guaranteed to remember their key insertion order, so you can omit importing the OrderedDict.

You can also do this:

distl2 = [l2[l1.index(key)] for key in distl1]

Python 3.6+

Dictionaries are ordered in Python 3.6+, as an implementation detail in 3.6 and confirmed in 3.7+. So in this case you can use dict with an iterable which ignores duplicates. To ignore duplicates, you can use the itertools unique_everseen recipe, also available via 3rd party more_itertools.unique_everseen or toolz.unique:

from operator import itemgetter
from toolz import unique

l1 = ['a','b','a','e','b','c','a','d']
l2 = ['t1','t2','t3','t4','t5','t6','t7','t8']

keys, values = zip(*dict(unique(zip(l1, l2), key=itemgetter(0))).items())

print(keys)

('a', 'b', 'e', 'c', 'd')

print(values)

('t1', 't2', 't4', 't6', 't8')

Python 2.7

You can use collections.OrderedDict instead of dict for Python 2.7, where dictionaries are not ordered:

from collections import OrderedDict

keys, values = zip(*OrderedDict(unique(zip(l1, l2), key=itemgetter(0))).items())

The question doesn't say if you need to preserve the order. If not, list of unique values of l1 would be:

distl1 = list(set(l1))

And the corresponding values of l2:

distl2 = [l2[l1.index(value)] for value in distl1]

(where index() always returns the first occurrence)

The resulting lists will keep your logical mapping, in the random order:

['b', 'e', 'c', 'd', 'a']
['t2', 't4', 't6', 't8', 't1']

EDIT:

Another approach (no dictionaries, no index() in a loop, order preserved, 2.7 friendly):

l1 = ['a','b','a','e','b','c','a','d']
l2 = ['t1','t2','t3','t4','t5','t6','t7','t8']

distl1 = []
distl2 = []

for i, val in enumerate(l1):
    if val not in distl1:
        distl1.append(val)
        distl2.append(l2[i])
Related