Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ruby / splitting array into columns

I could've found out using the right naming, but so far nothing popped up. I'd look into splitting an array into columns.

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

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

Practically, I'd look into iterating over an active record array, displaying those (they're images records) in 4 different html columns, while maintaining their order from left to right

like image 558
Ben Avatar asked Dec 14 '15 01:12

Ben


2 Answers

Rails solution

In rails, you can simply do:

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 rails method added to Array class, which behaves very similar to each_slice, with the difference being that it guarantees all the arrays to be the same size. It is important here so we can use transpose later on. This method returns an array of your rows.

Now, transpose - another cool method worth knowing. It expects an array of arrays, and all inner arrays must be the same length (so in fact this represents a rectangular matrix). What it does - it returns an array of columns in target array.

Now we need to get rid of nils (unless they don't bother you), so we run compact on each of the columns and we have exactly what you wanted.

Pure ruby solution:

We don't have in_groups_of so we need to implement same behaviour without it:

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

Better solution to practical issue

Practically, I'd look into iterating over an active record array, displaying those (they're images records) in 4 different html columns, while maintaining their order from left to right

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

In your case, I would suggest 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
  }
}

Main advantage of this model is that you can make the number of column dependent on media queries, so display different number of images per row depending on your user screen size (important for mobiles).

If you feel more adventurous and you hate IE and don't care for it's users, go and check out flex-boxes as well. One day it might become a standard and can be seen on many pages already.

like image 137
BroiSatse Avatar answered Sep 22 '22 08:09

BroiSatse


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 have posted a blog post on Ruby: Arrays by Example for a visual resource.

like image 35
6ft Dan Avatar answered Sep 26 '22 08:09

6ft Dan