3

I am attempting to merge two python lists, where their values at a given index will form a list (element) in a new list. For example:

merge_lists([1,2,3,4], [1,5]) = [[1,1], [2,5], [3], [4]]

I could iterate on this function to combine ever more lists. What is the most efficient way to accomplish this?

Edit (part 2)

Upon testing the answer I had previously selected, I realized I had additional criteria and a more general problem. I would also like to combine lists containing lists or values. For example:

merge_lists([[1,2],[1]] , [3,4]) = [[1,2,3], [1,4]]

The answers currently provided generate lists of higher dimensions in cases like this.

David Ferris
  • 2,215
  • 6
  • 28
  • 53

4 Answers4

10

One option is to use itertools.zip_longest (in python 3):

from itertools import zip_longest    

[[x for x in t if x is not None] for t in zip_longest([1,2,3,4], [1,5])]
# [[1, 1], [2, 5], [3], [4]]

If you prefer sets:

[{x for x in t if x is not None} for t in zip_longest([1,2,3,4], [1,5])]
# [{1}, {2, 5}, {3}, {4}]

In python 2, use itertools.izip_longest:

from itertools import izip_longest    

[[x for x in t if x is not None] for t in izip_longest([1,2,3,4], [1,5])]
#[[1, 1], [2, 5], [3], [4]]

Update to handle the slightly more complicated case:

def flatten(lst):

    result = []
    for s in lst:
        if isinstance(s, list):
            result.extend(s)
        else:
            result.append(s)

    return result

This handles the above two cases pretty well:

[flatten(x for x in t if x is not None) for t in izip_longest([1,2,3,4], [1,5])]
# [[1, 1], [2, 5], [3], [4]]

[flatten(x for x in t if x is not None) for t in izip_longest([[1,2],[1]] , [3,4])]
# [[1, 2, 3], [1, 4]]

Note even though this works for the above two cases, but it can still break under deeper nested structure, since the case can get complicated very quickly. For a more general solution, you can see here.

Psidom
  • 209,562
  • 33
  • 339
  • 356
  • 2
    Is `if x` too broad, e.g. `0` would be removed? Perhaps an explicit test against `None`? – AChampion Jun 30 '17 at 21:16
  • 3
    Also, OP tagged this a Python 2.7, so it would be `izip_longest`. `zip_longest` is Python 3.x – Wondercricket Jun 30 '17 at 21:17
  • 1
    This answer is seriously awesome! I just threw it into a function like: `def merge_lists(a, b): return [[x for x in t if x is not None] for t in izip_longest(a, b)]` . Thank you!! – David Ferris Jun 30 '17 at 21:26
  • I have updated the description outlining a more general problem - could this solution be easily modified? – David Ferris Jun 30 '17 at 21:46
1

You could use itertools.izip_longest and filter():

>>> lst1, lst2 = [1, 2, 3, 4], [1, 5]
>>> from itertools import izip_longest
>>> [list(filter(None, x)) for x in izip_longest(lst1, lst2)]
[[1, 1], [2, 5], [3], [4]]

How it works: izip_longest() aggregates the elements from two lists, filling missing values with Nones, which you then filter out with filter().

Eugene Yarmash
  • 142,882
  • 41
  • 325
  • 378
1

Another way to have your desired output using zip():

 def merge(a, b):
     m = min(len(a), len(b))
     sub = []
     for k,v in zip(a,b):
         sub.append([k, v])
     return sub + list([k] for k in a[m:]) if len(a) > len(b) else sub + list([k] for k in b[m:])

a = [1, 2, 3, 4]
b = [1, 5]
print(merge(a, b))
>>> [[1, 1], [2, 5], [3], [4]]
Chiheb Nexus
  • 9,104
  • 4
  • 30
  • 43
1

Another way using zip_longest and chain from itertools:

import itertools
[i for i in list(itertools.chain(*itertools.zip_longest(list1, list2, list3))) if i is not None]

or in 2 lines (more readable):

merged_list = list(itertools.chain(*itertools.zip_longest(a, b, c)))
merged_list = [i for i in merged_list if i is not None]
Rani
  • 6,424
  • 1
  • 23
  • 31