迭代不同长度的一对迭代的最干净的方法,包装较短的可迭代? [重复]

问题描述 投票:-1回答:2

这个问题在这里已有答案:

如果我有两个不同长度的迭代,我怎样才能最干净地配对它们,重新使用较短的值,直到消耗掉更长的所有值?

例如,给出两个列表

l1 = ['a', 'b', 'c']
l2 = ['x', 'y']

希望有一个函数fn()导致成对:

>>> fn(l1, l2)
[('a', 'x'), ('b', 'y'), ('c', 'x')]

我发现我可以编写一个函数来执行此操作

def fn(l1, l2):
    if len(l1) > len(l2):
        return [(v, l2[i % len(l2)]) for i, v in enumerate(l1)]
    return [(l1[i % len(l1)], v) for i, v in enumerate(l2)]

>>> fn(l1, l2)
[('a', 'x'), ('b', 'y'), ('c', 'x')]
>>> l2 = ['x', 'y', 'z', 'w']
>>> fn(l1,l2)
[('a', 'x'), ('b', 'y'), ('c', 'z'), ('a', 'w')]

但是,我很贪婪并且好奇其他方法是什么?所以我可以选择最明显和优雅,并警惕其他人。

itertools.zip_longest在很多类似的问题中提出的非常接近我想要的用例,因为它有一个fillvalue参数,它将填充较长的对。但是,这只需要一个值,而不是回绕到较短列表中的第一个值。

作为一个注释:在我的用例中,一个列表总是比另一个列表短得多,这可能允许快捷方式,但通用解决方案也会令人兴奋!

python python-3.x iterable
2个回答
1
投票

您可以使用itertools.cycle()zip来获得所需的行为。

正如itertools.cycle()文件所说,它:

使迭代器返回迭代中的元素并保存每个元素的副本。当iterable耗尽时,返回保存副本中的元素。

例如:

>>> l1 = ['a', 'b', 'c']
>>> l2 = ['x', 'y']

>>> from itertools import cycle
>>> zip(l1, cycle(l2))
[('a', 'x'), ('b', 'y'), ('c', 'x')]

因为在你的情况下,l1l2的长度可能会有所不同,你的通用fn()应该是这样的:

from itertools import cycle

def fn(l1, l2):
    return zip(l1, cycle(l2)) if len(l1) > len(l2) else zip(cycle(l1), l2)

样品运行:

>>> l1 = ['a', 'b', 'c']
>>> l2 = ['x', 'y']

# when second parameter is shorter 
>>> fn(l1, l2)
[('a', 'x'), ('b', 'y'), ('c', 'x')]

# when first parameter is shorter
>>> fn(l2, l1)
[('x', 'a'), ('y', 'b'), ('x', 'c')]

-1
投票

如果你不确定哪一个是最短的,next it.cycle两个列表中最长的len

def fn(l1, l2):
    return (next(zip(itertools.cycle(l1), itertoools.cycle(l2))) for _ in range(max((len(l1), len(l2)))))

>>> list(fn(l1, l2))

[('a', 'x'), ('a', 'x'), ('a', 'x')]

itertools.cycle将无限重复列表。然后,zip将两个无限列表放在一起,以获得您想要的循环,但无限重复。所以现在,我们需要将其修剪到合适的尺寸。 max((len(l1), len(l2)))将找到两个列表中最长的长度,然后next无限可迭代,直到你达到正确的长度。请注意,这将返回一个生成器,因此要获得您想要的输出,请使用list来使用该函数。

© www.soinside.com 2019 - 2024. All rights reserved.