How do I reorganize a list back with the help of a dict

Viewed 335

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

7 Answers

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']]

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]

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.

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']]

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']]
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']]

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']]
Related