読者です 読者をやめる 読者になる 読者になる

【Scala】groupBy関数でやさしいグルーピング

groupBy関数についてちょっと色々試してみる。

最近よくわからない関数はREPLで試すということを学習した。
RubyとかLispではよく活用していたのだが、Scalaではすっかり失念していた。



groupByというのはコレクションメソッドで、
キーでグルーピングしたMapを返します。

なんかようわからん。
てか、scalaのメソッドは説明読んでも全く使い方のイメージが湧いてこない。
単純にscalaのことよくわかってないってのが理由だけども、悔しいねぇ


とりあえず使ってみるのが一番!

val peopleList 
= List(("taro", 21, "Tokyo"), ("hanako", 19, "Saitam"), ("tanaka", 19, "Tokyo"))

サンプルとしてリストを用意した。
このリストは、(名前、年齢、出身地)という形式になっていて、3人分入っている。

これを出身地ごとにグループ化してみる。

val result = peopleList.groupBy(_._3)
println(result)
Map(Tokyo -> List((taro,21,Tokyo), (tanaka,19,Tokyo)), Saitam -> List((hanako,19,Saitam)))

TokyoとSaitamaというkeyでグループ化されたMapが生成されていることが確認できた。

もう少し詳しく見ていこう。
groupBy関数やmap関数、foreachなどのコレクション関数は引数に関数を受け取り、
コレクションの各要素に適用した結果を返す。
なんとまあわかりにくい説明だろうか。

peopleList.groupBy(n => n._3)

groupBy関数はコレクションメソッドなので、コレクションの各要素を取り出す。
まず1週目にpeopleListの1つ目の要素である、("taro", 21, "Tokyo")という要素が、
groupBy(n => n._3)のnに入る。
そしてn._3でnの3つ目の要素をkeyとして返している。
つまり、
("taro", 21, "Tokyo")の3つ目の要素"Tokyo"をkeyとする。

この動きを順々に繰り返し、重複したkeyの場合はグループ化するので、
結果として出身地でグループ化することができた。

最初のサンプルでは、

peopleList.groupBy(_._3)

と書いたが、これは各要素を_(アンダースコア)で受け取り、その_3を指定しているだけ。


そして、このようにグループ化して出身地のリストだけ得たい場合には、keysを使う。

val lives = peopleList.groupBy(_._3).keys
println(lives)
Set(Tokyo, Saitam)

Setになって取得できていることがわかる。
Setは重複を許さないリスト型だ。つまり取得できたkeyは重複されていないことがここでもわかる。

では逆に、valueの方を取得したい場合はというと、valuesを使う。

val values = peopleList.groupBy(_._3).values
println(values)
MapLike(List((taro,21,Tokyo), (tanaka,19,Tokyo)), List((hanako,19,Saitam)))

MapLikeの中にそれぞれのvalueのリストが入っている。



では今度は年齢でもグループ化してみよう。
出身地と同じことをするだけ。

val result = peopleList.groupBy(_._2)
println(result)
Map(19 -> List((hanako,19,Saitam), (tanaka,19,Tokyo)), 21 -> List((taro,21,Tokyo)))

今度は2番目の要素を指定してあげるだけ。
ばっちりグループ化できました。