Đan xen nhiều danh sách có cùng độ dài trong Python


85

Trong Python, có cách nào tốt để xen kẽ hai danh sách có cùng độ dài không?

Nói rằng tôi được cho [1,2,3][10,20,30]. Tôi muốn chuyển đổi chúng thành [1,10,2,20,3,30].


Không được khuyến khích, nhưng cố gắng này:it = iter(l1); list((yield next(it)) or i for i in l2)
Chris_Rands

Câu trả lời:


112

Sau khi đăng câu hỏi, tôi nhận ra rằng tôi có thể đơn giản làm như sau:

[val for pair in zip(l1, l2) for val in pair]

ở đâu l1l2là hai danh sách.


Nếu có N danh sách để xen kẽ, thì

lists = [l1, l2, ...]
[val for tup in zip(*lists) for val in tup]

5
chỉ hoạt động nếu l1 và l2 có cùng một số yếu tố
Emmanuel

14
@Emmanuel: Câu hỏi có nội dung "Trong Python, có cách nào tốt để xen kẽ hai danh sách có cùng độ dài không?"
NPE

1
Nếu bạn muốn thêm vào danh sách dài nhất, hãy sử dụng izip_longestcho python2 và zip_longestcho python3 ` [val for pair in itertools.zip_longest(l1, l2) for val in pair]kết quả với['a', 'b', 'a', 'b', 'a', 'b', None, 'b', None, 'b', None, 'b']
Sergey Zakharov

71

Đối với Python> = 2.3, có cú pháp lát cắt mở rộng :

>>> a = [0, 2, 4, 6, 8]
>>> b = [1, 3, 5, 7, 9]
>>> c = a + b
>>> c[::2] = a
>>> c[1::2] = b
>>> c
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Dòng c = a + bđược sử dụng như một cách đơn giản để tạo một danh sách mới có độ dài chính xác (ở giai đoạn này, nội dung của nó không quan trọng). Hai dòng tiếp theo thực hiện công việc đan xen thực tế ab: dòng đầu tiên gán các phần tử của atất cả các chỉ mục được đánh số chẵn của c; cái thứ hai gán các phần tử của bcho tất cả các chỉ mục được đánh số lẻ của c.


25

Được

a = [1, 2, 3]
b = [10, 20, 30]
c = [100, 200, 300, 999]

Giả sử các danh sách có độ dài bằng nhau, bạn có thể nhận được một danh sách xen kẽ với itertools.chainzip:

import itertools


list(itertools.chain(*zip(a, b)))
# [1, 10, 2, 20, 3, 30]

Giải pháp thay thế

itertools.zip_longest

Nói chung hơn với các danh sách không bằng nhau, hãy sử dụng zip_longest(được khuyến nghị):

[x for x in itertools.chain(*itertools.zip_longest(a, c)) if x is not None]
# [1, 100, 2, 200, 3, 300, 999]

Nhiều danh sách có thể được xen kẽ một cách an toàn:

[x for x in itertools.chain(*itertools.zip_longest(a, b, c)) if x is not None]
# [1, 10, 100, 2, 20, 200, 3, 30, 300, 999]

more_itertools+

Một thư viện roundrobincung cấp công thức itertools, interleaveinterleave_longest.

import more_itertools


list(more_itertools.roundrobin(a, b))
# [1, 10, 2, 20, 3, 30]

list(more_itertools.interleave(a, b))
# [1, 10, 2, 20, 3, 30]

list(more_itertools.interleave_longest(a, c))
# [1, 100, 2, 200, 3, 300, 999]

yield from

Cuối cùng, đối với một cái gì đó thú vị trong Python 3 (mặc dù không được khuyến khích):

list(filter(None, ((yield from x) for x in zip(a, b))))
# [1, 10, 2, 20, 3, 30]

list([(yield from x) for x in zip(a, b)])
# [1, 10, 2, 20, 3, 30]

+ Cài đặt bằngpip install more_itertools


8

Tôi cần một cách để làm điều này với danh sách các kích thước khác nhau mà câu trả lời được chấp nhận không giải quyết.

Giải pháp của tôi sử dụng một máy phát điện và việc sử dụng nó trông đẹp hơn một chút vì nó:

def interleave(l1, l2):
    iter1 = iter(l1)
    iter2 = iter(l2)
    while True:
        try:
            if iter1 is not None:
                yield next(iter1)
        except StopIteration:
            iter1 = None
        try:
            if iter2 is not None:
                yield next(iter2)
        except StopIteration:
            iter2 = None
        if iter1 is None and iter2 is None:
            raise StopIteration()

Và cách sử dụng:

>>> a = [1, 2, 3, 4, 5]
>>> b = ['a', 'b', 'c', 'd', 'e', 'f', 'g']
>>> list(interleave(a, b))
[1, 'a', 2, 'b', 3, 'c', 4, 'd', 5, 'e', 'f', 'g']
>>> list(interleave(b, a))
['a', 1, 'b', 2, 'c', 3, 'd', 4, 'e', 5, 'f', 'g']

Công roundrobinthức từ itertoolsmô-đun là một phần mở rộng tổng quát hơn của điều này.
ShadowRanger

7

Thay thế:

>>> l1=[1,2,3]
>>> l2=[10,20,30]
>>> [y for x in map(None,l1,l2) for y in x if y is not None]
[1, 10, 2, 20, 3, 30]

Điều này hoạt động vì bản đồ hoạt động trên các danh sách song song. Nó hoạt động giống nhau dưới 2.2. Tự nó, với Nonenhư các hàm được gọi, maptạo ra một danh sách các bộ giá trị:

>>> map(None,l1,l2,'abcd')
[(1, 10, 'a'), (2, 20, 'b'), (3, 30, 'c'), (None, None, 'd')]

Sau đó, chỉ cần làm phẳng danh sách các bộ giá trị.

Tất nhiên, ưu điểm là map sẽ hoạt động với bất kỳ số lượng danh sách nào và sẽ hoạt động ngay cả khi chúng có độ dài khác nhau:

>>> l1=[1,2,3]
>>> l2=[10,20,30]
>>> l3=[101,102,103,104]
>>> [y for x in map(None,l1,l2,l3) for y in x if y in not None]
[1, 10, 101, 2, 20, 102, 3, 30, 103, 104]

1
if ycũng sẽ lọc ra 0, if y is not Noneít dễ vỡ hơn.
Jochen Ritzel,

@Jochen Ritzel: Cảm ơn! Tôi đồng ý với bạn. Đã sửa. Tôi đã viết nó chỉ có cơ bắp tham gia ...
con sói

3

Tôi thích giải pháp của aix nhất. đây là một cách khác mà tôi nghĩ nên hoạt động trong 2.2:

>>> x=range(3)
>>> x
[0, 1, 2]
>>> y=range(7,10)
>>> y
[7, 8, 9]
>>> sum(zip(x,y),[])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can only concatenate list (not "tuple") to list
>>> sum(map(list,zip(x,y)),[])
[0, 7, 1, 8, 2, 9]

và một cách nữa:

>>> a=[x,y]
>>> [a[i][j] for j in range(3) for i in (0,1)]
[0, 7, 1, 8, 2, 9]

và:

>>> sum((list(i) for i in zip(x,y)),[])
[0, 7, 1, 8, 2, 9]

0
[el for el in itertools.chain(*itertools.izip_longest([1,2,3], [4,5])) if el is not None]

Miễn là bạn không có Nonemà bạn muốn giữ


0

Để trả lời tiêu đề của câu hỏi là "Xen kẽ nhiều danh sách có cùng độ dài trong Python", chúng ta có thể khái quát câu trả lời gồm 2 danh sách của @ekhumoro. Điều này yêu cầu rõ ràng rằng các danh sách có cùng độ dài, không giống như giải pháp (thanh lịch) của @NPE

import itertools

def interleave(lists):
    """Interleave a list of lists.

    :param lists: List of lists; each inner length must be the same length.
    :returns: interleaved single list
    :rtype: list

    """
    if len(set(len(_) for _ in lists)) > 1:
        raise ValueError("Lists are not all the same length!")
    joint = list(itertools.chain(*lists))
    for l_idx, li in enumerate(lists):
        joint[l_idx::len(lists)] = li
    return joint

Ví dụ:

>>> interleave([[0,2,4], [1, 3, 5]])
[0, 1, 2, 3, 4, 5]
>>> interleave([[0,2,4], [1, 3, 5], [10, 11, 12]])
[0, 1, 10, 2, 3, 11, 4, 5, 12]
>>> interleave([[0,2,4], [1, 3, 5], [10, 11, 12], [13, 14, 15]])
[0, 1, 10, 13, 2, 3, 11, 14, 4, 5, 12, 15]
>>> interleave([[0,2,4], [1, 3, 5], [10, 11, 12], [13, 14]])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 10, in interleave
ValueError: Lists are not all the same length!
>>> interleave([[0,2,4]])
[0, 2, 4]

0

Đến bữa tiệc quá muộn và có rất nhiều câu trả lời hay nhưng tôi cũng muốn cung cấp một giải pháp đơn giản bằng cách sử dụng extend()phương pháp:

list1 = [1, 2, 3]
list2 = [10, 20, 30]

new_list = []
for i in range(len(list1)):
    new_list.extend([list1[i], list2[i]])
print(new_list)

Đầu ra:

[1, 10, 2, 20, 3, 30]
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.