How can I transpose different sized ruby arrays?
A similar answer was posted (but deleted) an hour earlier:
arr = [[1, 2, 3], [4, 5], [6]]
arr[0].zip(*arr[1..-1])
#=> [[1, 4, 6], [2, 5, nil], [3, nil, nil]]
The above is equivalent to:
[1, 2, 3].zip([4, 5], [6])
This approach assumes that your first sub-array is always the longest. Otherwise the result will be truncated:
arr = [[1, 2], [3, 4, 5], [6]]
arr[0].zip(*arr[1..-1])
#=> [[1, 3, 6], [2, 4, nil]] missing: [nil, 5, nil]
Why does Ruby have zip and transpose when they do the same thing?
#transpose
Assumes that self is an array of arrays and transposes the rows and columns.
#zip
assumes self
can be any Enumerable
object.
More differences are here
a = [12,11,21]
b = [1,2]
[a,b].transpose # transpose': element size differs (2 should be 3) (IndexError)
a.zip(b) # => [[12, 1], [11, 2], [21, nil]]
b.zip(a) # => [[1, 12], [2, 11]]
That to apply the #transpose
method a
and b
should be of the same size. But for applying #zip
, it is not needed b
to be of the same size of a
, ie b
and a
can be of any of size.
With #zip
, the resultant array size will always be the size of self
. With #transpose
the resulting array size will be any of the inner array's size of self
.
How do I transpose multiple arrays unless an array element is empty?
Consider this :
arr1 = ["a", "b"]
arr2 = ["", "c"]
arr3 = ["d", "e"]
Now, as per your requirement,; you want those tranposed arrays where arr2
blank value is not present.
Use #reject
to do that as:
[arr1, arr2, arr3].transpose.reject{ |x| x[1].empty? }
Here, x[1]
corresponds to the second element in each transposed array; and it comes from arr2
; so here, we rejected all those instances where ""
was present in arr2
.
Hope it helps
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
Ruby array from two different sized arrays?
bus_times = days.zip(trips.each_slice(3)).flatten
or if you want to keep them as an array of arrays:
bus_times = days.zip(trips.each_slice(3)).map(&:flatten)
What is the best way to merge two arrays (element + element), if elements itself are arrays
[Array1, Array2].transpose.map(&:flatten)
=> [[1, 2, 1, 4], [8, 11], [2, 3, 3, 6]]
RubyGuides: "Turn Rows Into Columns With The Ruby Transpose Method"
Each step explained:
[Array1, Array2]
=> [[[1, 2], [], [2, 3]],
[[1, 4], [8, 11], [3, 6]]]
Create a grid like array.
[Array1, Array2].transpose
=> [[[1, 2], [1, 4]], [[], [8, 11]], [[2, 3], [3, 6]]]
transpose switches rows and columns (close to what we want)
[Array1, Array2].transpose.map(&:flatten)
=> [[1, 2, 1, 4], [8, 11], [2, 3, 3, 6]]
flatten gets rid of the unnecessary nested arrays (here combined with map to access nested arrays)
How to swap columns and rows in a matrix with Ruby
Move j = 0
within the i
loop
def my_transpose(matrix)
new_matrix = []
i = 0
while i < matrix.size
new_matrix[i] = []
j = 0 # move this here
while j < matrix.size
new_matrix[i] << matrix[j][i]
j += 1
end
i += 1
end
return new_matrix
end
If j
is not reset to 0 for every i
loop then it never enters the j
loop except for the first time:
i = 0
j = 0
# Enter i loop
new_matrix[0] = []
# Enter j loop
new_matrix[0] << matrix[0][0]
j += 1 #=> 1
new_matrix[0] << matrix[1][0]
j += 1 #=> 2
new_matrix[0] << matrix[2][0]
j += 1 #=> 3
# Exit j loop
i += 1 #=> 1
new_matrix[1] = []
# Does not enter j loop as j = 3 > matrix.size
i += 1 #=> 2
new_matrix[2] = []
# Does not enter j loop as j = 3 > matrix.size
i += 1 #=> 3
# Exit i loop
Converting uneven rows to columns with FasterCSV
I would insert nulls to fill the holes in your matrix, something such as:
a = [[1, 2, 3], [3, 4]]
# This would throw the error you're talking about
# a.transpose
# Largest row
size = a.max { |r1, r2| r1.size <=> r2.size }.size
# Enlarge matrix inserting nils as needed
a.each { |r| r[size - 1] ||= nil }
# So now a == [[1, 2, 3], [3, 4, nil]]
aa = a.transpose
# aa == [[1, 3], [2, 4], [3, nil]]
Related Topics
Rails 5 Db Migration: How to Fix Activerecord::Concurrentmigrationerror
Ruby: Building a Plot of Function
Rails Shows "Warning: Can't Verify Csrf Token Authenticity" from a Restkit Post
How to Serialize an Object Using Tcpserver Inside
Difference Between Lambda and -> Operator in Ruby
Opening Several Threads with Watir-Webdriver Results in 'Connection Refused' Error
Your Ruby Version Is 2.1.0, But Your Gemfile Specified 2.0.0
How to Read Only X Number of Bytes of the Body Using Net::Http
Method Invocation in Class Definition
What Are the Ruby Win32API Parameters | How to Pass a Null Pointer
Capybara Synchronize with Has_No_Css
Ruby on Rails Routing Matching Username
How to Store Nil User's Goal in a Session
Rails G Migration Doesn't Work