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

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

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

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

我们可以这样做:

 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或转置或其他一些?

如果源数组中没有nil ,你只需要用nil扩展第一个数组,zip会自动用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 

这是一个稍微复杂的版本,如果数组包含nil

 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 

这是一种更简单的方法。 它利用了将数组传递给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] 

你的实现对我来说很好。 您可以使用#zip通过向数组填充一些垃圾值,压缩它们,然后展平并移除垃圾实现此目的。 但那是令人费解的IMO。 你在这里所拥有的是清洁和自我解释,它只需要被重复。

编辑 :修复了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]