如何在python中优雅地交错两个不等长的列表?

jdg4fx2g  于 2022-12-20  发布在  Python
关注(0)|答案(9)|浏览(172)

我想在python中合并两个不同长度的列表,这样较短的列表中的元素在最终的列表中尽可能的等间距。也就是说,我想取[1, 2, 3, 4]['a','b']并合并它们,得到一个类似于[1, 'a', 2, 3, 'b', 4]的列表。它也需要能够处理不是精确倍数的列表。所以它可以取[1, 2, 3, 4, 5]['a', 'b', 'c'],并产生[1, 'a', 2, 'b', 3, 'c', 4, 5]或类似的结果,它需要保持两个列表的顺序。
我可以看到如何通过一个冗长的蛮力方法来做到这一点,但由于Python似乎有大量的优秀工具来做各种各样的聪明的事情,我不知道(还),我想知道是否有什么更优雅的我可以使用?
如果要在末尾保留多余的元素,请参见How to interleave two lists of different length?

9avjhtql

9avjhtql1#

大量借鉴Jon Clements的解决方案,可以编写一个函数,接受任意数量的序列,并返回均匀间隔项的合并序列:

import itertools as IT

def evenly_spaced(*iterables):
    """
    >>> evenly_spaced(range(10), list('abc'))
    [0, 1, 'a', 2, 3, 4, 'b', 5, 6, 7, 'c', 8, 9]
    """
    return [item[1] for item in
            sorted(IT.chain.from_iterable(
            zip(IT.count(start=1.0 / (len(seq) + 1), 
                         step=1.0 / (len(seq) + 1)), seq)
            for seq in iterables))]

iterables = [
    ['X']*2,
    range(1, 11),
    ['a']*3
    ]

print(evenly_spaced(*iterables))

收益率

[1, 2, 'a', 3, 'X', 4, 5, 'a', 6, 7, 'X', 8, 'a', 9, 10]
piwo6bdm

piwo6bdm2#

这基本上与Bresenham's line algorithm相同,您可以计算“像素”位置并将其用作列表的索引。
任务的不同之处在于每个元素只显示一次,需要修改算法或对索引进行后处理,只在元素第一次出现时追加列表中的元素,但有一点不明确:当两个像素/列表索引同时改变时,你需要选择先包含哪一个。这对应于问题和注解中提到的两个不同的列表交错选项。

sulc1iza

sulc1iza3#

假设a是要插入的序列:

from itertools import izip, count
from operator import itemgetter
import heapq

a = [1, 2, 3, 4]
b = ['a', 'b']

fst = enumerate(a)
snd = izip(count(0, len(a) // len(b)), b)
print map(itemgetter(1), heapq.merge(fst, snd))
# [1, 'a', 2, 3, 'b', 4]
mo49yndu

mo49yndu4#

如果a是较长的列表而b是较短的列表

from itertools import groupby

len_ab = len(a) + len(b)
groups = groupby(((a[len(a)*i//len_ab], b[len(b)*i//len_ab]) for i in range(len_ab)),
                 key=lambda x:x[0])
[j[i] for k,g in groups for i,j in enumerate(g)]

例如

>>> a = range(8)
>>> b = list("abc")
>>> len_ab = len(a) + len(b)
>>> groups = groupby(((a[len(a)*i//len_ab], b[len(b)*i//len_ab]) for i in range(len_ab)), key=lambda x:x[0])
>>> [j[i] for k,g in groups for i,j in enumerate(g)]
[0, 'a', 1, 2, 'b', 3, 4, 5, 'c', 6, 7]

您可以使用此技巧来确保ab

b, a = sorted((a, b), key=len)
qaxu7uf2

qaxu7uf25#

如果我们这样修改“乔恩”的回答

from itertools import count
import heapq

[x[1] for x in heapq.merge(izip(count(0, len(b)), a), izip(count(0, len(a)), b))]

a/b中哪个最长并不重要

uqjltbpv

uqjltbpv6#

如果我们想在没有itertools的情况下执行此操作:

def interleave(l1, l2, default=None):  
    max_l = max(len(l1), len(l2))
    data  = map(lambda x: x + [default] * (max_l - len(x)), [l1,l2])
    return [data[i%2][i/2] for i in xrange(2*max_l)]

啊,漏掉了等间距的部分。由于某种原因,这个问题被标记为重复,在不同的列表长度存在的情况下,这个问题不需要等间距。

ajsxfq5m

ajsxfq5m7#

我喜欢unutbu's answer,但不喜欢嵌套样式,所以我重写了它。当我在那里的时候,我注意到排序不稳定,所以我用operator.itemgetter修复了它。
我还用enumerate替换了itertools.count,因为它更直观,而且它对大输入也更准确,虽然我还没有测试过。

import itertools
import operator

def distribute(sequence):
    """
    Enumerate the sequence evenly over the interval (0, 1).

    >>> list(distribute('abc'))
    [(0.25, 'a'), (0.5, 'b'), (0.75, 'c')]
    """
    m = len(sequence) + 1
    for i, x in enumerate(sequence, 1):
        yield i/m, x

def intersperse(*sequences):
    """
    Evenly intersperse the sequences.

    Based on https://stackoverflow.com/a/19293603/4518341

    >>> list(intersperse(range(10), 'abc'))
    [0, 1, 'a', 2, 3, 4, 'b', 5, 6, 7, 'c', 8, 9]
    >>> list(intersperse('XY', range(10), 'abc'))
    [0, 1, 'a', 2, 'X', 3, 4, 'b', 5, 6, 'Y', 7, 'c', 8, 9]
    >>> ''.join(intersperse('hlwl', 'eood', 'l r!'))
    'hello world!'
    """
    distributions = map(distribute, sequences)
    get0 = operator.itemgetter(0)
    for _, x in sorted(itertools.chain(*distributions), key=get0):
        yield x

请注意,与第二个示例有一个不同之处,在第二个示例中,'b''c'下移:

>>> list(intersperse(range(1, 6), 'abc'))
[1, 'a', 2, 3, 'b', 4, 'c', 5]
juud5qan

juud5qan8#

@Jon Clements答案的变体,使用带解释的more_itertools.collate

    • 鉴于**
import itertools as it

import more_itertools as mit

a, b = range(1, 5), ["a", "b"]
    • 代码**
first = enumerate(a)
second = zip(it.count(0, len(a) // len(b)), b)
[x for i, x in mit.collate(first, second, key=lambda x: x[0])]
# [1, 'a', 2, 3, 'b', 4]
    • 详细信息**

这个答案已经更新,可以在Python 3中使用。
firstsecond是元组的可迭代,每个元组包括位置-元素对。

list(first)
# [(0, 1), (1, 2), (2, 3), (3, 4)]

list(second)
# [(0, 'a'), (2, 'b')]

more_itertools.collate() Package heapq.merge(),它按顺序合并预先排序的firstsecond可迭代对象。在最终列表解析中,key是排序函数,而返回每个元组中的最后一个元素。

    • 另请参阅**
  • more_itertools.interleave_evenly()基于前面提到的Bresenham算法。您必须确定输出是否是您想要的。
  • heapq.merge()并直接使用。

通过> pip install more_itertools安装此第三方软件包

noj0wjuj

noj0wjuj9#

此函数将生成任意数量列表的均匀混合,而不依赖于昂贵的sort

from typing import Any, Iterable, List

def evenly_mix(*lists: List[Any]) -> Iterable[Any]:
    m = max(len(b) for b in lists)
    deltas = [len(b) / m for b in lists]
    cnts, psums = [[0 for _ in lists] for _ in [0, 0]]
    for i in range(m):
        for j, b in enumerate(lists):
            psums[j] += deltas[j]
            if psums[j] >= cnts[j] + 1:
                yield b[cnts[j]]
                cnts[j] += 1

相关问题