所以我有这个(排序很好的(数组。
有时我需要数组中的所有元素。但其他时候,我需要所有偶数索引的成员在一起,所有奇数索引的成员也在一起。再说一遍,有时我需要把它分成三组,一组的指数为0,3,6等,然后是1,4,7,最后是2,5,8。
这可以用group_by
和取指数的模数来完成。亲眼目睹:
https://play.crystal-lang.org/#/r/4kzj
arr = ['a', 'b', 'c', 'd', 'e']
puts arr.group_by { |x| arr.index(x).not_nil! % 1 } # {0 => ['a', 'b', 'c', 'd', 'e']}
puts arr.group_by { |x| arr.index(x).not_nil! % 2 } # {0 => ['a', 'c', 'e'], 1 => ['b', 'd']}
puts arr.group_by { |x| arr.index(x).not_nil! % 3 } # {0 => ['a', 'd'], 1 => ['b', 'e'], 2 => ['c']}
但里面的not_nil!
感觉像是一种代码气味/警告,有更好的方法。
我可以在不需要查找和处理Nil类型的情况下获得元素的索引吗?
您也可以只做:
arr = ['a', 'b', 'c', 'd', 'e']
i = 0
puts arr.group_by { |x| i += 1; i % 1 }
i = 0
puts arr.group_by { |x| i += 1; i % 2 }
i = 0
puts arr.group_by { |x| i += 1; i % 3 }
除了nillable返回类型之外,为每个元素调用Array#index
也是非常低效的。这意味着O(N²(的运行时间。
#group_by
用于按值分组,但您不需要该值进行分组,因为您只想按索引分组。这比包裹#group_by
和#index
要容易得多
一个更有效的解决方案是在索引上循环,并根据索引对值进行分组:
groups = [[] of Char, [] of Char]
arr.each_index do |i|
groups[i % 2] << arr[i]
end
对此没有特殊的方法,但您自己实现它相当简单。
如果您不需要所有的组,而只需要其中一个组,那么还可以使用Int32#step
每隔一个索引迭代一次:
group = [] of Char
2.step(to: arr.size - 1, by: 3) do |i|
group << arr[i]
end