Replace all values in list of lists according to positions in another list

Viewed 68

Suppose we have a list, and a list of lists. I want to replace all values in each sub-list of the latter such that the value i is replaced by the value in the i-th position of the former. For example:

l = [4,2,1,0,3]
l_of_l = [[0,3],[4,1,2,3],[2,4]]

desired_output = [[4,0],[3,2,1,0],[1,3]]

All 0's are replaced by l[0], which equals 4. All 1's are replacd by l[1], which equals 2. And so on.

All the numbers appear just once in l, and all numbers between the maximum and the minimum are there. Namely, if the minimum value is 0 and the maximum is 6, then the list will be a shuffled version of list(range(7)). Also, all numbers present in the lists that l_of_l contains are present in l (i.e. if the maximum value in l is 6, there will not be any number greater than 6 in any list of l_of_l).

I've done this:

for this_l in l_of_l:
    for i in range(len(this_l)):
        this_l[i] = l[this_l[i]]

But I'm not very fond of loops. Is there a more pythonic way to do this?

3 Answers

You can use a list comprehension:

l = [4,2,1,0,3]
l_of_l = [[0,3],[4,1,2,3],[2,4]]
result = [[l[b] for b in i] for i in l_of_l]

Output:

[[4, 0], [3, 2, 1, 0], [1, 3]]

Or, if you wish to avoid explicit loops entirely, you can use map:

result = list(map(lambda x:list(map(lambda c:l[c], x)), l_of_l))

Output:

[[4, 0], [3, 2, 1, 0], [1, 3]]

Although the latter is not nearly as clean as a list comprehension.

A list comprehension is always an option, although it won't be in-place like your solution:

result = [[l[x] for x in row] for row in l_of_l]

I know you said you didn't want a fully recursive solution, but here is one that works for arbitrarily nested iterables containing integers and other iterables:

from functools import partial

def lookup(table, values):
    try:
        return list(map(partial(lookup, table), values))
    except TypeError:
        return table[values]

IDEOne Link: https://ideone.com/atwfnO

You can avoid an explicit inner for loop by using map with list.__getitem__:

L = [4,2,1,0,3]
LoL = [[0,3],[4,1,2,3],[2,4]]

res = [list(map(L.__getitem__, i)) for i in LoL]

[[4, 0], [3, 2, 1, 0], [1, 3]]
Related