【问题标题】:Interleave multiple lists of the same length in Python在Python中交错多个相同长度的列表
【发布时间】:2011-12-18 07:24:38
【问题描述】:

在 Python 中,有没有一种很好的方法来交错两个相同长度的列表?

假设我得到了[1,2,3][10,20,30]。我想把它们转换成[1,10,2,20,3,30]

【问题讨论】:

标签: python list


【解决方案1】:

发布问题后,我意识到我可以简单地执行以下操作:

[val for pair in zip(l1, l2) for val in pair]

其中l1l2 是两个列表。


如果有 N 个列表要交错,那么

lists = [l1, l2, ...]
[val for tup in zip(*lists) for val in tup]

【讨论】:

  • 仅当 l1 和 l2 具有相同数量的元素时才有效
  • @Emmanuel:问题是“在 Python 中,有没有一种很好的方法可以将两个 长度相同的列表交错?”
  • 如果您想填充到最长列表,请使用izip_longest 用于python2 和zip_longest 用于python3 `[val for pair in itertools.zip_longest(l1, l2) for val in pair] 结果与['a', 'b', 'a', 'b', 'a', 'b', None, 'b', None, 'b', None, 'b']
  • 在 for 循环中,它的工作方式类似于:for i, j in zip(x,y):
  • [*zip(l1,l2)] 在 Python 3.8 中运行良好。
【解决方案2】:

对于 Python>=2.3,有 extended slice syntax:

>>> 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]

c = a + b 行是一种简单的方法来创建一个长度正好合适的新列表(在这个阶段,它的内容并不重要)。接下来的两行执行了交错ab 的实际工作:第一行将a 的元素分配给c 的所有偶数索引;第二个将b 的元素分配给c 的所有奇数索引。

【讨论】:

    【解决方案3】:

    给定

    a = [1, 2, 3]
    b = [10, 20, 30]
    c = [100, 200, 300, 999]
    

    代码

    假设列表长度相等,您可以获得itertools.chainzip 的交错列表:

    import itertools
    
    
    list(itertools.chain(*zip(a, b)))
    # [1, 10, 2, 20, 3, 30]
    

    替代品

    itertools.zip_longest

    对于不相等的列表,更一般地,使用zip_longest(推荐):

    [x for x in itertools.chain(*itertools.zip_longest(a, c)) if x is not None]
    # [1, 100, 2, 200, 3, 300, 999]
    

    许多列表可以安全地交错:

    [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]
    

    more_itertools+

    roundrobin itertools 配方、interleaveinterleave_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

    最后,关于 Python 3 中一些有趣的东西(虽然不推荐):

    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]
    

    +使用pip install more_itertools安装

    【讨论】:

      【解决方案4】:

      我需要一种方法来处理已接受的答案未解决的不同大小的列表。

      我的解决方案使用了一个生成器,因此它的使用看起来更好一些:

      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()
      

      及其用法:

      >>> 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']
      

      【讨论】:

      【解决方案5】:

      替代方案:

      >>> 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]
      

      这是因为map 可以并行处理列表。它works the same 低于 2.2。单独使用 None 作为被调用函数,map 会生成一个元组列表:

      >>> map(None,l1,l2,'abcd')
      [(1, 10, 'a'), (2, 20, 'b'), (3, 30, 'c'), (None, None, 'd')]
      

      然后将元组列表展平。

      当然,map 的优点是适用于任意数量的列表,即使它们的长度不同也可以:

      >>> 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 也会过滤掉0if y is not None 不那么脆弱。
      • @Jochen Ritzel:谢谢!我同意你的看法。固定的。我只用了肌肉来写它......
      【解决方案6】:

      我最喜欢 aix 的解决方案。这是我认为应该在 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]
      

      还有一种方法:

      >>> a=[x,y]
      >>> [a[i][j] for j in range(3) for i in (0,1)]
      [0, 7, 1, 8, 2, 9]
      

      和:

      >>> sum((list(i) for i in zip(x,y)),[])
      [0, 7, 1, 8, 2, 9]
      

      【讨论】:

      • list(sum(zip(x,y),())) 会更好,不是吗?
      【解决方案7】:

      一个有趣的方法是使用heapq.merge,将最终列表中的位置作为键:

      from heapq import merge
      from itertools import count
      
      a = [1,2,3]
      b = [10,20,30]
      
      counter = count()
      res = list(merge(a, b, key=lambda x: next(counter)))
      print(res)
      

      输出

      [1, 10, 2, 20, 3, 30]
      

      对于多个列表,你可以解压它们:

      from heapq import merge
      from itertools import count
      
      a = [1, 2, 3]
      b = [10, 20, 30]
      c = [11, 21, 31]
      
      counter = count()
      res = list(merge(*[a, b, c], key=lambda x: next(counter)))
      print(res)
      

      输出

      [1, 10, 11, 2, 20, 21, 3, 30, 31]
      

      【讨论】:

        【解决方案8】:
        [el for el in itertools.chain(*itertools.izip_longest([1,2,3], [4,5])) if el is not None]
        

        只要你没有想要保留的None

        【讨论】:

          【解决方案9】:

          要回答题为“在 Python 中交错多个相同长度的列表”的问题,我们可以概括 @ekhumoro 的 2-list 答案。这明确要求列表的长度相同,这与@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
          

          例子:

          >>> 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]
          

          【讨论】:

            【解决方案10】:

            聚会太晚了,有很多好的答案,但我也想提供一个使用extend()方法的简单解决方案:

            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)
            

            输出:

            [1, 10, 2, 20, 3, 30]
            

            【讨论】:

              【解决方案11】:

              这也是一种方法:

              list1 = [1, 2, 3]
              list2 = [10, 20, 30]
              
              list(sum(zip(list1, list2), ()))
              

              想法类似。

              1. 将列表压缩在一起。 (使用 zip)
              2. 展平为元组(使用 sum(..., ())
              3. 转换为列表

              【讨论】:

                猜你喜欢
                • 2018-11-21
                • 2018-05-27
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2018-03-17
                • 1970-01-01
                相关资源
                最近更新 更多