14

I have the following code:

list1 = [('a', 0), ('b', 100), ('c', 200), ('d', 300), ('e', 400), ('f', 500)]
list2 = [[0, 200, 400], [100, 300, 500]]

list2 just basically reorganizes the numbers into teams, the 2 sublists.

My list3 would then be:

list3 = [['a', 'c', 'e'], ['b', 'd', 'f']]

So by looking up the values in list2 in list1, what code do I need to produce list3?

This is also valid:

list1 = [('a', 0), ('b', 0), ('c', 0), ('d', 0), ('e', 0), ('f', 0)]
list2 = [[0, 0, 0], [0, 0, 0]]

It would give:

list3 = [['a', 'b', 'c'], ['d', 'e', 'f']]

So basically 'a' and 'f' could have the same value but they can only return once in list3

Felix
  • 1,837
  • 9
  • 26
hduu2929
  • 149
  • 3

7 Answers7

8

A possibility is to use collections.defaultdict with collections.deque:

from collections import defaultdict, deque
def to_num(a, b):
  d = defaultdict(deque)
  for j, k in a:
     d[k].append(j)
  return [[d[l].popleft() for l in i] for i in b]

list1 = [('a', 0), ('b', 100), ('c', 200), ('d', 300), ('e', 400), ('f', 500)]
list2 = [[0, 200, 400], [100, 300, 500]]
print(to_num(list1, list2))

Output:

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

With your second test case:

list1 = [('a', 0), ('b', 0), ('c', 0), ('d', 0), ('e', 0), ('f', 0)]
list2 = [[0, 0, 0], [0, 0, 0]]
print(to_num(list1, list2))

Output:

[['a', 'b', 'c'], ['d', 'e', 'f']]
Ajax1234
  • 69,937
  • 8
  • 61
  • 102
  • Why do you use a deque instead of a list? Is it because `deque.popleft()` is faster than `list.pop(0)`? – wjandrea May 08 '20 at 19:42
  • 1
    @wjandrea `deque.popleft()` is indeed faster than `list.pop(0)`, see [here](https://stackoverflow.com/questions/32543608/deque-popleft-and-list-pop0-is-there-performance-difference). Additionally, I think that a "remove from the front" operation best fits the `deque` paradigm. – Ajax1234 May 08 '20 at 20:01
4

You can first collect first values of elements of list1 depending on second element of values of list1:

from collections import deque, defaultdict
byvalue = defaultdict(deque)
for name, value in list1:
    byvalue[value].append(name)

then you can collect back the result by processing list2

list3 = [[byvalue[value].popleft() for value in x]
         for x in list2]
6502
  • 112,025
  • 15
  • 165
  • 265
3

Here's an idea using pop to avoid duplicate items:

def pop_element(inp_list, search_value): 
    for index, (_, query_value) in enumerate(inp_list): 
        if query_value == search_value: 
            return inp_list.pop(index)[0] 

list1 = [('a', 0), ('b', 100), ('c', 200), ('d', 300), ('e', 400), ('f', 500)]
list2 = [[0, 200, 400], [100, 300, 500]]
list3 = [[pop_element(list1, value) for value in inner_list] for inner_list in list2]

# list 3 is [['a', 'c', 'e'], ['b', 'd', 'f']]

It also works on your other example:

list1 = [('a', 0), ('b', 0), ('c', 0), ('d', 0), ('e', 0), ('f', 0)]   
list2 = [[0, 0, 0], [0, 0, 0]]                                                                                                                                
list3 = [[pop_element(list1, value) for value in inner_list] for inner_list in list2]

# list3 is [['a', 'b', 'c'], ['d', 'e', 'f']]

And it conveniently places None values when a value from list2 is not found:

list1 = [('a', 0), ('b', 0), ('c', 0), ('d', 0), ('e', 0), ('f', 0)]   
list2 = [[0, 0, 11], [0, 'this is a value', 0]]                                                                                                                                
list3 = [[pop_element(list1, value) for value in inner_list] for inner_list in list2]

# list3 is [['a', 'b', None], ['c', None, 'd']]

The only caveat is that this method turns list1 into an empty list, so you might want to do list1_copy = list1.copy() and pass that to the list comprehension instead.

jfaccioni
  • 7,099
  • 1
  • 9
  • 25
1

Like this:

In [220]: list3 = []

In [221]: dict1 = dict(list1)

In [225]: for i in list2:
     ...:     l = [] 
     ...:     for j in i: 
     ...:         l.append(list(dict1.keys())[list(dict1.values()).index(j)]) 
     ...:     list3.append(l) 
     ...:     

In [226]: list3
Out[226]: [['a', 'c', 'e'], ['b', 'd', 'f']]
wjandrea
  • 28,235
  • 9
  • 60
  • 81
Mayank Porwal
  • 33,470
  • 8
  • 37
  • 58
1

Using lamdba function and generator

def find(x):
    " find matching value of x in lst2 " 
    return next(t[0] for t in list1 if t[1]==x)

list3 = [[find(a) for a in sublist] for sublist in list2]

print(list3)
# out: [['a', 'c', 'e'], ['b', 'd', 'f']]
DarrylG
  • 16,732
  • 2
  • 17
  • 23
  • [Named lambdas are bad practice](https://stackoverflow.com/a/38381663/4518341). Use a `def` instead. – wjandrea May 08 '20 at 19:25
  • 1
    @wjandrea--agreed, and understand from [PEP 8](https://docs.quantifiedcode.com/python-anti-patterns/correctness/assigning_a_lambda_to_a_variable.html) but like the brevity here. – DarrylG May 08 '20 at 19:28
0
list1 = [('a', 0), ('b', 100), ('c', 200), ('d', 300), ('e', 400), ('f', 500)]

list2 = [[0, 200, 400], [100, 300, 500]]
list3 = []

#Loop through each sublist in list 2
for sublist in list2:
    #creating an inner list which will go in list3
    inner = []
    for el in sublist:
        for (index, (title, label)) in enumerate(list1):
            if label == el:
                inner.append(title)
                #remove the first element that matches the current label
                list1.pop(index)
                break

    list3.append(inner)

print(list3)

Output

[['a', 'c', 'e'], ['b', 'd', 'f']]
Frankie
  • 48
  • 2
  • 7
0

The easiest way is using collections.OrderedDict

code:

list1 = [('a', 0), ('b', 100), ('c', 200), ('d', 300), ('e', 400), ('f', 500)]
list2 = [[0, 200, 400], [100, 300, 500]]
list4 = list2[:]
l1_as_dict = OrderedDict(list1)
for key,value in l1_as_dict.items():

    #spread out list,find the first position,then break double-loop
    for i,a_item in enumerate(list2):
        for j,item in enumerate(list2[i]):
            if value == item:
                list4[i][j] = key
                break
        else:
            continue
        break
print(list4)

output:

[['a', 'c', 'e'], ['b', 'd', 'f']]
daichaoyang
  • 21
  • 1
  • 6