Iterate over two lists with different lengths

2020-07-22 04:55发布

问题:

I have 2 lists of numbers that can be different lengths, for example:

list1 = [1, 2, -3, 4, 7]
list2 = [4, -6, 3, -1]

I need to iterate over these with the function:

final_list = []
for index in range(???):
    if list1[index] < 0:
        final_list.insert(0, list1[index])
    elif list1[index] > 0:
        final_list.insert(len(final_list), list1[index])
    if list2[index] < 0:
        final_list.insert(0, list2[index])
    elif list2[index] > 0:
        final_list.insert(len(final_list), list2[index])
return final_list

but can't figure out how to deal with the range as the shorter list will become "out of range" if I use the max length. Any thoughts on how to overcome this or how to change my function?

回答1:

itertools.zip_longest(*iterables, fillvalue=None) will do the job for you:

If the iterables are of uneven length, missing values are filled-in with fillvalue.

For your example lists, this will yield:

>>> import itertools
>>> list1 = [1, 2, -3, 4, 7]
>>> list2 = [4, -6, 3, -1]

>>> for combination in itertools.zip_longest(list1, list2):
    print(combination)

(1, 4)
(2, -6)
(-3, 3)
(4, -1)
(7, None)

If you only want to use as many values as are present in both lists, use the built-in zip():

The iterator stops when the shortest input iterable is exhausted.

>>> for combination in zip(list1, list2):
    print(combination)

(1, 4)
(2, -6)
(-3, 3)
(4, -1)


回答2:

You can process adjacent items from the lists by using itertools.zip_longest() (itertools.izip_longest() if using Python 2) to produce a sequence of paired items. Pairs will be padded with None for lists of mismatched length.

Then you can simplify the code in the body of the loop by flattening the sequence of paired items and filtering out the None values, and in your case, 0 values. That's what the generator expression below does.

Then it's just a matter of appending or inserting values into final_list if greater or less than zero respectively.

In code:

from itertools import zip_longest

final_list = []
for value in (i for pair in zip_longest(list1, list2) for i in pair if i):
    if value > 0:
        final_list.append(value)
    else:
        final_list.insert(0, value)

print(final_list)
[-1, -3, -6, 1, 4, 2, 3, 4, 7]

Notice that this will filter out any zero values that might be present in the lists. If you want to keep these then modify the generator expression to filter out the None values only:

(i for pair in zip_longest(list1, list2)
    for i in pair if i is not None)

and modify the body of the loop to insert the 0 wherever it should go in final_list.



回答3:

In your case you should probably just check if the index is longer than the sequence:

list1 = [1, 2, -3, 4, 7]
list2 = [4, -6, 3, -1]

final_list = []
for index in range(max(len(list1), len(list2))):
    if index < len(list1):
        if list1[index] < 0:
            final_list.insert(0, list1[index])
        elif list1[index] > 0:
            final_list.insert(len(final_list), list1[index])

    if index < len(list2):
        if list2[index] < 0:
            final_list.insert(0, list2[index])
        elif list2[index] > 0:
            final_list.insert(len(final_list), list2[index])

print(final_list)
# [-1, -3, -6, 1, 4, 2, 3, 4, 7]

Or use itertools.zip_longest (or itertools.izip_longest on python-2.x) and check for some fillvalue (i.e. None):

import itertools

list1 = [1, 2, -3, 4, 7]
list2 = [4, -6, 3, -1]

final_list = []
for item1, item2 in itertools.zip_longest(list1, list2, fillvalue=None):
    if item1 is None:
        pass
    elif item1 < 0:
        final_list.insert(0, item1)
    elif item1 > 0:
        final_list.append(item1)

    if item2 is None:
        pass
    elif item2 < 0:
        final_list.insert(0, item2)
    elif item2 > 0:
        final_list.append(item2)

However your approach skips items when they are == 0, that's probably an oversight but you should check that it's working correct with zeros.

Also you use insert a lot. The last elifs could instead use final_list.append(item1) (or item2), like I did in the second example.

In this case the handling for item1 and item2 is identical, so you could use another loop:

import itertools

list1 = [1, 2, -3, 4, 7]
list2 = [4, -6, 3, -1]

final_list = []
for items in itertools.zip_longest(list1, list2, fillvalue=None):
    for item in items:
        if item is None:
            pass
        elif item < 0:
            final_list.insert(0, item)
        elif item > 0:
            final_list.append(item)