代码之家  ›  专栏  ›  技术社区  ›  ChrisInEdmonton

如何在Ruby中交织不同长度的数组

  •  7
  • ChrisInEdmonton  · 技术社区  · 16 年前

    如果我想在Ruby中交错一组数组,并且每个数组的长度相同,我们可以这样做:

    a.zip(b).zip(c).flatten
    

    但是,如果阵列的大小不同,我们如何解决这个问题?

    我们可以这样做:

    def interleave(*args)
      raise 'No arrays to interleave' if args.empty?
      max_length = args.inject(0) { |length, elem| length = [length, elem.length].max }
      output = Array.new
      for i in 0...max_length
        args.each { |elem|
          output << elem[i] if i < elem.length
        }
      end
      return output
    end
    

    但是有没有更好的“Ruby”方法,也许是使用zip或转置之类的?

    3 回复  |  直到 16 年前
        1
  •  7
  •   John La Rooy    16 年前

    如果源数组没有 nil 在它们中,您只需要使用 无 无 . 这也意味着你可以使用 compact 清除多余的条目,希望比显式循环更有效

    def interleave(a,*args)
        max_length = args.map(&:size).max
        padding = [nil]*[max_length-a.size, 0].max
        (a+padding).zip(*args).flatten.compact
    end
    

    这里有一个稍微复杂一点的版本,如果数组 做 包含

    def interleave(*args)
        max_length = args.map(&:size).max
        pad = Object.new()
        args = args.map{|a| a.dup.fill(pad,(a.size...max_length))}
        ([pad]*max_length).zip(*args).flatten-[pad]
    end
    
        2
  •  7
  •   David J. user3890638    13 年前

    这里有一个更简单的方法。它利用了数组传递到的顺序 zip

    def interleave(a, b)
      if a.length >= b.length
        a.zip(b)
      else
        b.zip(a).map(&:reverse)
      end.flatten.compact
    end
    
    interleave([21, 22], [31, 32, 33])
    # => [21, 31, 22, 32, 33]
    
    interleave([31, 32, 33], [21, 22])
    # => [31, 21, 32, 22, 33]
    
    interleave([], [21, 22])
    # => [21, 22]
    
    interleave([], [])
    # => []
    

    请注意:这将删除所有 nil

    interleave([11], [41, 42, 43, 44, nil])
    # => [11, 41, 42, 43, 44]
    
        3
  •  5
  •   thorncp    16 年前

    使用#zip实现这一点,方法是用一些垃圾值填充数组,压缩它们,然后扁平化并移除垃圾。但在我看来,这太复杂了。你这里的东西是干净的,不言自明的,只需要涂上红色就行了。

    编辑 :修复了booboo。

    def interleave(*args)
      raise 'No arrays to interleave' if args.empty?
      max_length = args.map(&:size).max
      output = []
      max_length.times do |i|
        args.each do |elem|
          output << elem[i] if i < elem.length
        end
      end
      output
    end
    
    a = [*1..5]
    # => [1, 2, 3, 4, 5]
    b = [*6..15]
    # => [6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
    c = [*16..18]
    # => [16, 17, 18]
    
    interleave(a,b,c)
    # => [1, 6, 16, 2, 7, 17, 3, 8, 18, 4, 9, 5, 10, 11, 12, 13, 14, 15]
    

    :为了好玩

    def interleave(*args)
      raise 'No arrays to interleave' if args.empty?
      max_length = args.map(&:size).max
      # assumes no values coming in will contain nil. using dup because fill mutates
      args.map{|e| e.dup.fill(nil, e.size...max_length)}.inject(:zip).flatten.compact
    end
    
    interleave(a,b,c)
    # => [1, 6, 16, 2, 7, 17, 3, 8, 18, 4, 9, 5, 10, 11, 12, 13, 14, 15]