[英]Merge lists in Python by placing every nth item from one list and others from another?
我有兩個列表, list1
和list2
。
這里len(list2) << len(list1)
。
現在我想合並兩個列表,使得最終列表的每個第n個元素 來自list2
, 而其他 元素 來自list1
。
例如:
list1 = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
list2 = ['x', 'y']
n = 3
現在最后的清單應該是:
['a', 'b', 'x', 'c', 'd', 'y', 'e', 'f', 'g', 'h']
實現這一目標的最恐怖的方法是什么?
我想將list2
所有元素添加到最終列表中,最終列表應包括list1
和list2
所有元素。
使較大的列表成為迭代器可以輕松地為較小列表的每個元素獲取多個元素:
list1 = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
list2 = ['x', 'y']
n = 3
iter1 = iter(list1)
res = []
for x in list2:
res.extend([next(iter1) for _ in range(n - 1)])
res.append(x)
res.extend(iter1)
>>> res
['a', 'b', 'x', 'c', 'd', 'y', 'e', 'f', 'g', 'h']
這避免了insert
,這對於大型列表來說可能是昂貴的,因為每次需要重新創建整個列表。
要保留原始列表,可以嘗試以下操作:
result = copy.deepcopy(list1)
index = n - 1
for elem in list2:
result.insert(index, elem)
index += n
結果
['a', 'b', 'x', 'c', 'd', 'y', 'e', 'f', 'g', 'h']
使用itertools
模塊和補充的more_itertools
包 ,您可以通過幾種不同的方式構建可迭代的解決方案。 首先進口:
import itertools as it, more_itertools as mt
第一個似乎是最干凈的,但它依賴於more_itertools.chunked()
。
it.chain(*mt.roundrobin(mt.chunked(list1, n-1), list2))
這個只使用more_itertools.roundrobin()
,其實現來自itertools
文檔,因此如果您無權訪問more_itertools
您可以自己復制它。
mt.roundrobin(*([iter(list1)]*(n-1) + [list2]))
或者,這與第一個樣本more_itertools
而不使用任何more_itertools
特定的函數。 基本上, grouper
可以替代chunked
,但在某些情況下它會在最后添加None
,所以我將它包裹在其中。 it.takewhile
去除它們。 當然,如果你在實際上包含None
列表上使用它,它會在到達那些元素時停止,所以要小心。
it.takewhile(lambda o: o is not None,
it.chain(*mt.roundrobin(mt.grouper(n-1, list1), list2))
)
我在Python 3.4上測試了這些,但我相信這些代碼示例也適用於Python 2.7。
以下解決方案怎么樣? 但是我沒有更好的...
>>> list1 = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
>>> list2 = ['x', 'y']
>>> n = 2
>>> for i in range(len(list2)):
... list1.insert(n, list2[i])
... n += 3
...
...
>>> list1
['a', 'b', 'x', 'c', 'd', 'y', 'e', 'f', 'g', 'h']
n
是2,因為列表中第三個元素的索引是2,因為它從0開始。
list(list1[i-1-min((i-1)//n, len(list2))] if i % n or (i-1)//n >= len(list2) else list2[(i-1)//n] for i in range(1, len(list1)+len(list2)+1))
絕對不是pythonic,但我認為在單行中進行它可能很有趣。 更可讀(真的嗎?)版本:
list(
list1[i-1-min((i-1)//n, len(list2))]
if i % n or (i-1)//n >= len(list2)
else
list2[(i-1)//n]
for i in range(1, len(list1)+len(list2)+1)
)
基本上,一些修改索引並確定哪個列表和哪個索引從下一個元素。
另一種方法是,計算切片步驟:
list1 = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
list2 = ['x', 'y']
n = 3
res = []
m = n - 1
start, end = 0, m
for x in list2:
res.extend(list1[start:end])
res.append(x)
start, end = end, end + m
res.extend(list1[start:])
>>> res
['a', 'b', 'x', 'c', 'd', 'y', 'e', 'f', 'g', 'h']
list1 = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
list2 = ['x', 'y']
n = 3
new = list1[:]
for index, item in enumerate(list2):
new[n * (index + 1) - 1: n * (index + 1) - 1] = item
print(new)
我很佩服@David Z使用more_itertools
。 對工具的更新可以簡化解決方案:
import more_itertools as mit
n = 3
groups = mit.windowed(list1, n-1, step=n-1)
list(mit.flatten(mit.interleave_longest(groups, list2)))
# ['a', 'b', 'x', 'c', 'd', 'y', 'e', 'f', 'g', 'h']
摘要: list2
正在從list1
交錯到組中,最后扁平化為一個列表。
筆記
groups
: n-1
大小的滑動窗口,例如[('a', 'b'), ('c', 'd'), ('e', 'f'), ('g', 'h')]
interleave_longest
目前相當於roundrobin
None
是默認填充值。 可選擇使用filter(None, ...)
刪除filter(None, ...)
也許這是另一種解決方案,將list1
切成正確的索引,然后將list2
的元素添加到list1
。
>>> list1 = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
>>> list2 = ['x', 'y']
>>> n = 3
>>> for i in range(len(list2)):
... list1 = list1[:n*(i+1) - 1] + list(list2[i]) + list1[n*(i+1)-1:]
...
>>> list1
['a', 'b', 'x', 'c', 'd', 'y', 'e', 'f', 'g', 'h']
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.