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 nil
s, 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 = []
toz = x.class.new
to allowweave
'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 => ['-','-','-','-','-']
andy => ['b', 'c']
. - What remains of
x
is to be partitioned intoy.size+1.0 => 3.0
intervals, each withx.size/3.0 => 5/3.0 => 1.67
elements if the elements ofx
could be divided. As they can't be divided, and the larger groups are to be on the right, we strip off1.67.floor => 1
element fromx
, append that and the next element ofy
toz
, then repeat, untily.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
Ruby Daemons and Jruby - Alternative Options
Ruby: Yield Block from a Block
Create a Human-Readable List with "And" Inserted Before the Last Element from a Ruby List
Scraping/Parsing Google Search Results in Ruby
Phonegap Mobile Rails Authentication (Devise? Authentication from Scratch)
Ruby Create Tar Ball in Chunks to Avoid Out of Memory Error
Rails Active Record: Find in Conjunction with :Order and :Group
Rspec 'Eq' VS 'Eql' in 'Expect' Tests
Pg::Invalidparametervalue: Error: Invalid Value for Parameter "Client_Min_Messages": "Panic"
Is There a Method to Limit/Clamp a Number
Making a Module Inherit from Another Module in Ruby
How to Specify a Read Timeout for a Net::Http::Post.New Request in Ruby 2
Documentation for Psych To_Yaml Options
Open-Uri Returning Ascii-8Bit from Webpage Encoded in Iso-8859
Clicking Link with JavaScript in Mechanize
Heroku: How to Push to Specific App If You Have Multiple Apps in Heroku
Why Require Mongo Gives Me Loaderror: No Such File to Load -- Openssl