How to Interleave Arrays of Different Length in Ruby

How to interleave arrays of different length in Ruby

If the source arrays don't have nil in them, you only need to extend the first array with nils, zip will automatically pad the others with nil. This also means you get to use compact to clean the extra entries out which is hopefully more efficient than explicit loops

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

Here is a slightly more complicated version that works if the arrays do contain 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

Unequally interleave two ruby arrays

a.each_slice(2).zip(b).flatten
#=> [1, 2, "a", 3, 4, "b", 5, 6, "c"]

Merge and interleave two arrays in Ruby

You can do that with:

a.zip(s).flatten.compact

Ruby - Evenly distribute and interleave/interweave the elements of one array within another array of unknown length

Another way:

def weave(xx,yy)
x = xx.dup
y = yy.dup
n_extra = [y.size - x.size + 1, 0].max
y_extra = y.slice!(y.size - n_extra, n_extra)
z = x.class.new
loop do
z += (x.slice!(0,((x.size)/(y.size+1.0)).floor) + y.slice!(0,1))
break if y.empty?
end
z + x + y_extra
end

x = ('-'*10).chars
# => ["-", "-", "-", "-", "-", "-", "-", "-", "-", "-"]

p weave(x, ['a', 'b', 'c'])
# => ["-", "-", "a", "-", "-", "b", "-", "-", "-", "c", "-", "-", "-"]

p weave(x, ['a', 'b', 'c']).join # => "--a--b---c---"
p weave('----------', 'abc') # => "--a--b---c---"

y = (1..12).each_with_object([]) {|i,a| a << ('a'..'z').to_a.first(i)}

y.each {|e| p weave(x, e).join}
"-----a-----"
"---a---b----"
"--a--b---c---"
"--a--b--c--d--"
"-a-b--c--d--e--"
"-a-b-c-d--e--f--"
"-a-b-c-d-e-f--g--"
"-a-b-c-d-e-f-g-h--"
"-a-b-c-d-e-f-g-h-i-"
"-a-b-c-d-e-f-g-h-i-j"
"-a-b-c-d-e-f-g-h-i-jk"
"-a-b-c-d-e-f-g-h-i-jkl"

Edit: I made a few changes to my original solution:

  • added the first two lines of weave so the arguments would not be altered.
  • simplified the loop.
  • at @Kal's suggestion, removed .join from the end of (arr + x + y_extra).
  • changed z = [] to z = x.class.new to allow weave's arguments to be arrays or strings.

A word about ((x.size)/(y.size+1.0)).floor. Suppose x = ['-','-','-','-','-','-'] and y = ['a', 'b', 'c']. Then:

  • after the first iteration of the loop, z => ['-', 'a'], x => ['-','-','-','-','-'] and y => ['b', 'c'].
  • What remains of x is to be partitioned into y.size+1.0 => 3.0 intervals, each with x.size/3.0 => 5/3.0 => 1.67 elements if the elements of x could be divided. As they can't be divided, and the larger groups are to be on the right, we strip off 1.67.floor => 1 element from x, append that and the next element of y to z, then repeat, until y.empty? => true.

How to merge nested arrays of varying lengths, given they share an element in Ruby?

You could use a hash and convert it to an array

new_hash = {}
old_array.each do |o|
new_hash[o[0]] ||= []
o.each_with_index do |n, i|
new_hash[o[0]][i] ||= n
end
end

puts new_hash.inspect
puts new_hash.map { |k,v| v }.inspect

This will result in

{"00"=>["00", "apples", nil, 2, 8, 3], "01"=>["01", "bananas", 2, 5, 3]}
[["00", "apples", nil, 2, 8, 3], ["01", "bananas", 2, 5, 3]]

Separate an array of arrays to be passed into a method as multiple objects

What I think you're attempting to do can be accomplished by using the splat operator at the time of method invocation, like such:

hello(*my_array)

Here's a complete example:

def foo(a, *b)
puts a.inspect
puts b.inspect
end

foo(*[[1, 2], [3, 4], [5, 6]])

Prints the following:

[1, 2]
[[3, 4], [5, 6]]

Edit: Other solution

Now that you've pasted the source my opinion is that the method should be re-written to take a single parameter instead of using the splat operator in the parameters to pull out the first and rest. The reason is that if the length of the multidimensional array changes at runtime you'd be better off pulling out first and rest inside the method so you're not having to use the splat everywhere.

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

foo([[1, 2], [3, 4], [5, 6]])

Ruby, equally distribute elements and interleave/merge multiple arrays

I would use a sort to do this, based on element index postion, divided by size of array, plus some offset based on array id, to keep things consistent (if you don't need consistency, you could use a small random offset instead).

a = [:a,:b]
b = [:c]
c = [:d,:e,:f]
d = [:g:,:h,:i,:j]

def sort_pos array, id
(1..array.size).map { |i| (i - 0.5 + id/1000.0)/(array.size + 1e-6) }
end

# Combine all the arrays with their sort index, assigning ids to each array for consistency.
# Depending on how you receive these arrays, this structure can be built up programatically,
# as long as you add an array plus its sort index numbers at the same time
combined = (a + b + c + d).zip( sort_pos(a, 1) + sort_pos(b, 2) + sort_pos(c, 3) + sort_pos(d, 4) )

# Extract the values from the original arrays in their new order
combined.sort_by { |zipped| zipped[1] }.map { |zipped| zipped[0] }

=> [:g, :d, :a, :h, :e, :i, :b, :f, :j, :c]

There might be a cleaner way of doing this in Ruby . . . but I think the end result is what you are after - an "even" mix of multiple arrays.

If you only care about even-ness of mix from a statistical perspective (i.e. over time it is "fair"), you could just do this:

(a+b+c+d).shuffle

=> [:g, :b, :i, :c, :a, :h, :e, :j, :f, :d]

Create a Hash from two arrays of different sizes and iterate until none of the keys are empty

Here's an elegant one. You can "loop" the short array

longer  = [1, 2, 3, 4, 5, 6, 7]
shorter = ['a', 'b', 'c']

longer.zip(shorter.cycle).to_h # => {1=>"a", 2=>"b", 3=>"c", 4=>"a", 5=>"b", 6=>"c", 7=>"a"}

Combine colums into a nested array

If you want to keep the header close to its values, you could also use:

['cats', *cat].zip(['dogs', *dog])
#=> [["cats", "dogs"], ["cat1", "dog1"], ["cat2", "dog2"], ["cat3", "dog3"]]


Related Topics



Leave a reply



Submit