Ruby で ActiveSupport の in_groups_of を実装する
in_groups_of を Gem なしでやる方法がわからない
— masuyama13 (@masuyama_13) August 31, 2020
in_groups_of
っていうのは ActiveSupport にあるメソッドで each_slice
と同じように任意の要素数で配列を分割するんですが、足りない分を nil
で埋めるという特性があります。
[1, 2, 3].in_groups_of(2) # => [[1, 2], [3, nil]]
試しに自前で書いてみた。
using Module.new { refine Array do def in_groups_of(num, fill_with = nil, &block) # fill で足りない分を埋めてから each_slice する fill(fill_with, size, ((size + size % num) % num)).each_slice(num, &block).to_a end end } p (1..7).to_a.fill(nil, 7, (7 + 7 % 3) % 3).each_slice(3).to_a # => [[1, 2, 3], [4, 5, 6], [7, nil, nil]] p (1..6).to_a.in_groups_of(3) # => [[1, 2, 3], [4, 5, 6]] p (1..7).to_a.in_groups_of(3) # => [[1, 2, 3], [4, 5, 6], [7, nil, nil]] p (1..8).to_a.in_groups_of(3) # => [[1, 2, 3], [4, 5, 6], [7, 8, nil]] p (1..9).to_a.in_groups_of(3) # => [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
上のコードだと先に fill
で足りない分を埋めてから each_slice
で分割しています。
ちなみに ActiveSupport
の実装は以下のような感じでした。
# https://github.com/rails/rails/blob/fbe2433be6e052a1acac63c7faf287c52ed3c5ba/activesupport/lib/active_support/core_ext/array/grouping.rb#L22-L43 def in_groups_of(number, fill_with = nil) if number.to_i <= 0 raise ArgumentError, "Group size must be a positive integer, was #{number.inspect}" end if fill_with == false collection = self else # size % number gives how many extra we have; # subtracting from number gives how many to add; # modulo number ensures we don't add group of just fill. padding = (number - size % number) % number collection = dup.concat(Array.new(padding, fill_with)) end if block_given? collection.each_slice(number) { |slice| yield(slice) } else collection.each_slice(number).to_a end end
ちゃんと丁寧に書いてある。
ユースケースがあれば標準にもいれれそうな気がするけどどうだろうか。