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]
và [10,20,30]
. Tôi muốn chuyển đổi chúng thành [1,10,2,20,3,30]
.
Câu trả lời:
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 l1
và l2
là 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]
izip_longest
cho python2 và zip_longest
cho 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']
Đố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ế a
và b
: dòng đầu tiên gán các phần tử của a
tấ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 b
cho tất cả các chỉ mục được đánh số lẻ của c
.
Được
a = [1, 2, 3]
b = [10, 20, 30]
c = [100, 200, 300, 999]
Mã
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.chain
và zip
:
import itertools
list(itertools.chain(*zip(a, b)))
# [1, 10, 2, 20, 3, 30]
Giải pháp thay thế
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]
Một thư viện roundrobin
cung cấp công thức itertools, interleave
và interleave_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
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']
roundrobin
thức từ itertools
mô-đun là một phần mở rộng tổng quát hơn của điều này.
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 None
như các hàm được gọi, map
tạ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]
if y
cũng sẽ lọc ra 0
, if y is not None
ít dễ vỡ hơn.
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]
[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ó None
mà bạn muốn giữ
Để 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]
Đế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]
it = iter(l1); list((yield next(it)) or i for i in l2)