有没有办法在Crystal中group_bywith_index?

Is there a way to group_by with_index in Crystal?

所以我有了这个(排序良好的)数组。

有时我需要数组中的所有元素。但有时我需要将所有的偶数索引成员和所有奇数索引的成员放在一起。然后,有时我需要将它分成三组,其中一组的索引为 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类型就得到元素的索引?

除了nilable return 类型之外,为每个元素调用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

你也可以这样做:

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 }