通过放置一个列表中的每个第 n 个项目和另一个列表中的其他项目来合并 Python 中的列表?
Posted
技术标签:
【中文标题】通过放置一个列表中的每个第 n 个项目和另一个列表中的其他项目来合并 Python 中的列表?【英文标题】:Merge lists in Python by placing every nth item from one list and others from another? 【发布时间】:2016-04-14 01:29:32 【问题描述】:我有两个列表,list1
和 list2
。
这里len(list2) << len(list1)
。
现在我想合并这两个列表,使得最终列表的每个 nth 元素 都是 来自 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']
什么是最Pythonic 实现这一目标的方式?
我想将list2
的所有元素添加到最终列表中,最终列表应包含list1
和list2
中的所有元素。
【问题讨论】:
我相信您在此处发布的输出是针对n=2
而不是n=3
,nop?
@IronFist 我说第三个元素不是 index=3?
你对list2的大小有什么限制?您是在 list2 中循环还是仅将它们添加到 list1 直到它们用尽?这不是一个明显的问题。
【参考方案1】:
下面的解决方案怎么样?但是我没有更好的...
>>> 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 开始。
【讨论】:
【参考方案2】:要保留原始列表,您可以尝试以下操作:
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']
【讨论】:
【参考方案3】:使较大的列表成为迭代器可以很容易地为较小列表的每个元素获取多个元素:
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
,这对于大型列表来说可能会很昂贵,因为每次都需要重新创建整个列表。
【讨论】:
只是好奇,有什么理由不使用iter1.next()
而更喜欢next(iter1)
?
Python 3。始终使用next(obj)
,因为它适用于 Python 2 和 3。【参考方案4】:
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)
)
基本上,一些修改索引并确定从哪个列表和哪个索引获取下一个元素。
【讨论】:
【参考方案5】:使用itertools
module 和补充more_itertools
package,您可以通过几种不同的方式构建可迭代的解决方案。首先是导入:
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
特定的函数。基本上,grouper
可以替换chunked
,但在某些情况下它会在末尾添加None
s,所以我将它包装在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。
【讨论】:
复制列表迭代器并使用roundrobin
推进每个迭代器很聪明。【参考方案6】:
也许这里是另一种解决方案,将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']
【讨论】:
【参考方案7】:还有一种方法,计算切片步数:
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']
【讨论】:
【参考方案8】: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)
【讨论】:
【参考方案9】:我很欣赏@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, ...)
删除
【讨论】:
以上是关于通过放置一个列表中的每个第 n 个项目和另一个列表中的其他项目来合并 Python 中的列表?的主要内容,如果未能解决你的问题,请参考以下文章