Ruby / Column Array

I could find out using the correct name, but so far nothing has come of it. I would consider splitting an array into columns.

Meaning, having [0,1,2,3,4,5,6,7,9] , I would like to have 4 arrays (4 columns):

[ [0,4,9], [1,5], [2,6], [3,7] ]

In practice, I am viewing iteration over the active array of records, displaying those (image records) in 4 different html columns, preserving their order from left to right

+6
source share
6 answers

Rails Solution

In rails, you can simply:

 ary = [0,1,2,3,4,5,6,7,8,9] ary.in_groups_of(4).transpose.map(&:compact) #=> [[0, 4, 8], [1, 5, 9], [2, 6], [3, 7]] 

Explanation:

in_groups_of is a cool rail method added to the Array class, which is very similar to each_slice , with the difference that it ensures that all arrays are the same size. It is important here, so we can use transpose later. This method returns an array of your strings.

Now transpose is another cool method to know. It expects an array of arrays, and all internal arrays should be the same length (so in fact this is a rectangular matrix). What it does is it returns an array of columns in the target array.

Now we need to get rid of nils (if they do not bother you), so we run compact in each column, and we have exactly what you wanted.

Pure ruby โ€‹โ€‹solution:

We do not have in_groups_of , so we need to implement the same behavior without it:

 ary.each_slice(4).map {|a| a.fill nil, a.size, 4 - a.size}.transpose.map(&:compact) 

The best solution to a practical problem

In practice, I would look at iterating over the active array of records, showing these (they are image records) in 4 different html columns, while preserving the order from left to right

You should never use tables to display your data unless it is tabular data. Images are not tabular data, which means that the location of this particular image in a given column / row is completely irrelevant. There is always a better way to do this.

In your case, I would suggest a pure CSS solution (haml):

 %ul.images - @images.each do |i| %li= image_tag i.iamge_url # or sth 

Then in your css (scss):

 ul.images { width: 100%; font-size: 0; # remove whitespace between li elements li { display: inline-block; width: 25%; # forcing 25% without whitespaces guarantees 4 column } } 

The main advantage of this model is that you can specify the number of columns depending on media queries, so display a different number of images per row depending on the user's screen size (important for mobile phones).

If you feel more adventurous, and you hate IE and don't care about it, go in and check out the flexible boxes. Once it can become a standard and can already be seen on many pages.

+11
source

I have a short solution :-)

 x = [0, 1, 2, 3, 4, 5, 6, 7, 9] x.group_by.with_index {|_,index| index % 4 }.values # => [[0, 4, 9], [1, 5], [2, 6], [3, 7]] 

I posted a Ruby: Arrays by Example blog post for a visual resource.

+4
source

This should do the trick:

 input = [0,1,2,3,4,5,6,7,8,9] max_columns = 4 input.each_with_index.with_object([]) do | (val, index), output| i = index % max_columns (output[i] ||= []).push(val) end 
+2
source

or by slicing a large array into smaller arrays using SLICE

 input = Array(1 .. 33) output = input.each_slice(4).to_a output.each do |x| puts "#{x}" end 

outputs:

 [1, 2, 3, 4] [5, 6, 7, 8] [9, 10, 11, 12] [13, 14, 15, 16] [17, 18, 19, 20] [21, 22, 23, 24] [25, 26, 27, 28] [29, 30, 31, 32] [33] 

fyi, not just an array of arrays, but something to learn.

+1
source

I suggested that if the size of the array is not evenly divided by 4 , the first so-called truncated arrays should have the same size, and each of them has one more element than each of the remaining cut arrays. Here are some ways you could do this.

 arr = [1,2,3,4,5,6,7,8,9] 

Calculate the number of arrays having an additional element

 def split_it(n, arr) std_arr_size, nbr_bonus_arrays = arr.size.divmod(n) nbr_bonus_elements = nbr_bonus_arrays * (std_arr_size + 1) arr[0...nbr_bonus_elements].each_slice(std_arr_size+1).to_a + arr[nbr_bonus_elements..-1].each_slice(std_arr_size).to_a end split_it(1, arr) #=> [[1, 2, 3, 4, 5, 6, 7, 8, 9]] split_it(2, arr) #=> [[1, 2, 3, 4, 5], [6, 7, 8, 9]] split_it(3, arr) #=> [[1, 2, 3], [4, 5, 6], [7, 8, 9]] split_it(4, arr) #=> [[1, 2, 3], [4, 5], [6, 7], [8, 9]] split_it(5, arr) #=> [[1, 2], [3, 4], [5, 6], [7, 8], [9]] split_it(6, arr) #=> [[1, 2], [3, 4], [5, 6], [7], [8], [9]] split_it(7, arr) #=> [[1, 2], [3, 4], [5], [6], [7], [8], [9]] split_it(8, arr) #=> [[1, 2], [3], [4], [5], [6], [7], [8], [9]] split_it(9, arr) #=> [[1], [2], [3], [4], [5], [6], [7], [8], [9]] 

Use recursion

 def split_it(n, arr) return [arr] if n==1 m = (arr.size.to_f/n).ceil [arr.first(m)] + split_it(n-1, arr[m..-1]) end split_it(1, arr) #=> [[1, 2, 3, 4, 5, 6, 7, 8, 9]] split_it(2, arr) #=> [[1, 2, 3, 4, 5], [6, 7, 8, 9]] split_it(3, arr) #=> [[1, 2, 3], [4, 5, 6], [7, 8, 9]] split_it(4, arr) #=> [[1, 2, 3], [4, 5], [6, 7], [8, 9]] split_it(5, arr) #=> [[1, 2], [3, 4], [5, 6], [7, 8], [9]] split_it(6, arr) #=> [[1, 2], [3, 4], [5, 6], [7], [8], [9]] split_it(7, arr) #=> [[1, 2], [3, 4], [5], [6], [7], [8], [9]] split_it(8, arr) #=> [[1, 2], [3], [4], [5], [6], [7], [8], [9]] split_it(9, arr) #=> [[1], [2], [3], [4], [5], [6], [7], [8], [9]] 
+1
source

Pure Ruby Solution.

 def split(n, arr) arr.each_with_index.reduce(Array.new(n) { [] }) do |a, (e, i)| a[i%n] << e a end end 

UPDATE . Like an oneliner, as Carey suggested. It uses with_object() instead of reduce() and initializes the lowest array lazily when used.

 def split(n, arr) arr.each_with_index.with_object([]) { |(e, i), a| (a[i%n] ||= []) << e } end 
+1
source

All Articles