Ruby 2.4.0 リファレンスマニュアル > ライブラリ一覧 > 組み込みライブラリ > Enumerableモジュール > chunk

instance method Enumerable#chunk

chunk {|elt| ... } -> Enumerator[permalink][rdoc]
chunk(initial_state) {|elt, state| ... } -> Enumerator

要素を前から順にブロックで評価し、その結果によって 要素をチャンクに分けた(グループ化した)要素を持つ Enumerator を返します。

ブロックの評価値が同じ値が続くものを一つのチャンクとして 取り扱います。すなわち、ブロックの評価値が一つ前と 異なる所でチャンクが区切られます。

返り値の Enumerator は各チャンクのブロック評価値と 各チャンクの要素を持つ配列のペアを各要素とします。 そのため、eachだと以下のようになります。

enum.chunk {|elt| key }.each {|key, ary| ... }
enum.chunk(initial_state) {|elt, state| key }.each {|key, ary| ... }

例として、整数列を連続する奇数/偶数に分ける例を見てみます。 「n.even?」の値が切り替わるところで区切られているのがわかるでしょう。

[3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5].chunk {|n|
  n.even?
}.each {|even, ary|
  p [even, ary]
}
#=> [false, [3, 1]]
#   [true, [4]]
#   [false, [1, 5, 9]]
#   [true, [2, 6]]
#   [false, [5, 3, 5]]

このメソッドは各要素が既にソートされている場合に便利です。 以下の例では、テキスト辞書ファイル(中身がソートされている) に含まれる単語を先頭の文字ごとに数えています。

# line.ord は先頭の文字のコードポイントを返す
open("/usr/share/dict/words", "r:iso-8859-1") {|f|
  f.chunk {|line| line.ord }.each {|ch, lines| p [ch.chr, lines.length] }
}
#=> ["\n", 1]
#   ["A", 1327]
#   ["B", 1372]
#   ["C", 1507]
#   ["D", 791]
#   ...

さらにこのメソッドは以下の値を特別扱いします。

それ以外のアンダースコアで始まるシンボルを指定した場合は例外が発生します。

items.chunk { |item| :_underscore }
# => RuntimeError: symbols beginning with an underscore are reserved

nil、 :_separator はある要素を無視したい場合に用います。 例として svn log の出力のハイフンの所で区切りたい場合を考えます。

sep = "-"*72 + "\n" # ハイフンが72個の行
IO.popen("svn log README") {|f|
  f.chunk {|line|
    line != sep || nil
  }.each {|_, lines|
    pp lines
  }
}
#=> ["r20018 | knu | 2008-10-29 13:20:42 +0900 (Wed, 29 Oct 2008) | 2 lines\n",
#    "\n",
#    "* README, README.ja: Update the portability section.\n",
#    "\n"]
#   ["r16725 | knu | 2008-05-31 23:34:23 +0900 (Sat, 31 May 2008) | 2 lines\n",
#    "\n",
#    "* README, README.ja: Add a note about default C flags.\n",
#    "\n"]
#   ...

テキストを空行で区切られた段落に分けたい場合にも nil が使えます。

File.foreach("README").chunk {|line|
  /\A\s*\z/ !~ line || nil
}.each {|_, lines|
  pp lines
}

「:_alone」は要素を素通ししたい場合に用います。 以下の例では「Foo#bar」という形式の行が連続している場合のみ チャンク化し、それ以外は素通しします。

pat = /\A[A-Z][A-Za-z0-9_]+\#/
open(filename) {|f|
  f.chunk {|line| pat =~ line ? $& : :_alone }.each {|key, lines|
    if key != :_alone
      print lines.sort.join('')
    else
      print lines.join('')
    end
  }
}

チャンク化に状態遷移が必要な場合は、 オプション引数 initial_state に状態を保持するオブジェクトを渡します。 この場合、ブロックの第2引数にはこのオブジェクトが dup で複製 されて渡されます。

[PARAM] initial_state:
状態を保持するオブジェクト。この引数は deprecated です。
[EXCEPTION] RuntimeError:
予約されている値を用いた場合に発生します