Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Merge two or more lists with given order of merging

On start I have 2 lists and 1 list that says in what order I should merge those two lists. For example I have first list equal to [a, b, c] and second list equal to [d, e] and 'merging' list equal to [0, 1, 0, 0, 1].

That means: to make merged list first I need to take element from first list, then second, then first, then first, then second... And I end up with [a, d, b, c, e]. To solve this I just used for loop and two "pointers", but I was wondering if I can do this task more pythonic... I tried to find some functions that could help me, but no real result.

like image 595
Krzysztof Lewko Avatar asked May 18 '16 20:05

Krzysztof Lewko


People also ask

How do you merge three lists in python?

Using + operator The + operator does a straight forward job of joining the lists together. We just apply the operator between the name of the lists and the final result is stored in the bigger list. The sequence of the elements in the lists are preserved.


2 Answers

You could create iterators from those lists, loop through the ordering list, and call next on one of the iterators:

i1 = iter(['a', 'b', 'c'])
i2 = iter(['d', 'e'])
# Select the iterator to advance: `i2` if `x` == 1, `i1` otherwise
print([next(i2 if x else i1) for x in [0, 1, 0, 0, 1]]) # ['a', 'd', 'b', 'c', 'e']

It's possible to generalize this solution to any number of lists as shown below

def ordered_merge(lists, selector):
    its = [iter(l) for l in lists]
    for i in selector:
        yield next(its[i])
In [4]: list(ordered_merge([[3, 4], [1, 5], [2, 6]], [1, 2, 0, 0, 1, 2]))
Out[4]: [1, 2, 3, 4, 5, 6]

If the ordering list contains strings, floats, or any other objects that can't be used as list indexes, use a dictionary:

def ordered_merge(mapping, selector):
    its = {k: iter(v) for k, v in mapping.items()}
    for i in selector:
        yield next(its[i])
In [6]: mapping = {'A': [3, 4], 'B': [1, 5], 'C': [2, 6]}

In [7]: list(ordered_merge(mapping, ['B', 'C', 'A', 'A', 'B', 'C']))
Out[7]: [1, 2, 3, 4, 5, 6]

Of course, you can use integers as dictionary keys as well.


Alternatively, you could remove elements from the left side of each of the original lists one by one and add them to the resulting list. Quick example:

In [8]: A = ['a', 'b', 'c']
   ...: B = ['d', 'e']
   ...: selector = [0, 1, 0, 0, 1]
   ...: 

In [9]: [B.pop(0) if x else A.pop(0) for x in selector]
Out[9]: ['a', 'd', 'b', 'c', 'e']

I would expect the first approach to be more efficient (list.pop(0) is slow).

like image 156
vaultah Avatar answered Nov 16 '22 02:11

vaultah


How about this,

list1 = ['a', 'b', 'c']
list2 = ['d', 'e']
options = [0,1,0,0,1] 

list1_iterator = iter(list1)
list2_iterator = iter(list2)

new_list = [next(list2_iterator) if option else next(list1_iterator) for option in options]

print(new_list)
# Output
['a', 'd', 'b', 'c', 'e']
like image 31
SparkAndShine Avatar answered Nov 16 '22 02:11

SparkAndShine