Ruby / 将数组拆分为列

8
我本可以通过正确的命名找到答案,但是目前还没有结果。我会考虑将一个数组拆分为列。
即,将 [0,1,2,3,4,5,6,7,9] 拆分为 4 个数组(4 列):[ [0,4,9], [1,5], [2,6], [3,7] ] 实际上,我会遍历 active record 数组,在 4 个不同的 HTML 列中显示它们(它们是图片记录),同时保持其从左到右的顺序。

我觉得你的结果少了8。 :) - BroiSatse
你只关心有九个元素的数组吗?如果不是,你必须告诉我们你想要实现的规则是什么? - Cary Swoveland
@BroiSatse 是的,确实 (: - Ben
@CarySwoveland 一点也不。 - Ben
6个回答

18

Rails 解决方案

在 Rails 中,您可以简单地执行以下操作:

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]]

解释:

in_groups_of是一个很酷的Rails方法,添加到了Array类中,它的行为与each_slice非常相似,不同之处在于它保证所有数组具有相同的大小。这里很重要,因此我们可以稍后使用transpose方法。该方法返回您的行的数组。

现在,transpose - 另一个值得知道的酷方法。它期望一个数组数组,并且所有内部数组的长度必须相同(因此实际上代表一个矩形矩阵)。它的作用是返回目标数组中的列的数组。

现在我们需要除去nils(除非它们不打扰你),所以我们对每个列运行compact,然后我们就得到了您想要的东西。

纯Ruby解决方案:

我们没有in_groups_of,所以我们需要在没有它的情况下实现相同的行为:

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

实际问题的更好解决方案

实际上,我会考虑遍历一个活动记录数组,并在4个不同的html列中显示它们(它们是图像记录),同时保持它们从左到右的顺序。

除非数据是表格数据,否则您不应使用表格来显示数据。图像不是表格数据,这意味着特定图像在某个列/行中的位置完全无关紧要。总是有更好的方法来解决这个问题。

对于你的情况,我建议使用纯CSS解决方案(haml):

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

然后在你的 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
  }
}

这种模型的主要优点在于您可以根据媒体查询使列数成为变量,因此根据用户屏幕尺寸显示不同数量的图像(对于移动设备非常重要)。

如果您想冒险一些,并且您讨厌IE并且不关心它的用户,请去尝试一下flex-boxes。有一天它可能会成为标准,并且已经出现在许多页面上。


1
我无法让你的纯 Ruby 解决方案运行起来。对我而言,你的 each_slice 块返回的是 nil。 - 6ft Dan
1
@6ftDan - 感谢您的注意,现已修复。 - BroiSatse

5
我有一个简短的解决方案 :-)
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]]

我已经发布了一篇关于Ruby中数组的博客文章,该文章可以作为一个视觉资源,您可以通过链接查看。


之前我不知道你的解决方案。非常有创意! - Cary Swoveland

3
这应该可以解决问题:
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

我建议您在末尾添加行“output”,或者更好的方法是删除“output=[]”并将第4行更改为input.each_with_index.with_object([]) do |(val, index), output| - Cary Swoveland
根据您的建议进行了更改,我之前不知道有with_object方法,看起来非常有用! - hbejgel

1

或者通过将您的大数组切片成较小的数组 SLICE

input = Array(1 .. 33)

output = input.each_slice(4).to_a

output.each do |x| puts "#{x}" 
end

输出:

[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]

提供信息,不完全是一个数组的数组,但值得关注。


1
只要它仍然可以迭代,它就是一个“数组的数组”。 - Ben
幕后,是的 :) - z atef

1
我假设如果数组的大小不能被4整除,前面的几个切片数组将具有相同的大小,并且每个数组比其余的切片数组多一个元素。以下是几种实现方法。
arr = [1,2,3,4,5,6,7,8,9]

计算具有额外元素的数组数量。
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]] 

使用递归。
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
一个纯Ruby的解决方案。
def split(n, arr)
  arr.each_with_index.reduce(Array.new(n) { [] }) do |a, (e, i)|
    a[i%n] << e
    a
  end
end

更新: Cary建议使用一行代码,它使用with_object()代替reduce(),并在使用时惰性地初始化子数组。

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

1
你可以这样写:arr.each_with_index.with_object([]) { |(e,i),a| (a[i%n] ||= []) << e } - Cary Swoveland
@CarySwoveland 好建议。使用reduce()时,避免了额外的语句a来返回累积对象。下次会采纳这个建议。 - sschmeck

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接