[AD] Scalaアプリケーションの開発・保守は合同会社ミルクソフトにお任せください
Map
クラスには foreach
、foreachEntry
、tapEach
、map
など、
要素のそれぞれを処理するメソッドがいろいろありますが、
どれを使うべきなのか困ることがありますよね。
この記事では、Scalaの Map
クラスにおいて副作用の発生するような処理を行う際に使えるメソッドと、その使い分けについてご紹介します。
使い分け早わかり!
早速ですが、foreach
、foreachEntry
、tapEach
、map
の使い分けは以下のようになります。
副作用のある処理をする場合に:
- 処理結果を後続の処理では利用しないとき
- 処理にキーと値のタプルは不要なとき
foreachEntry
を使う
- 処理にキーと値のタプルが必要となるとき
foreach
を使う
- 処理にキーと値のタプルは不要なとき
- 処理結果を後続の処理で使いたいとき
map
を使う
- 処理結果ではなく元々の要素を後続の処理に渡したいとき
tapEach
を使う
以下のマップを題材に具体的な使い方を見てみましょう。
case class Person(firstName: String, lastName: String, age: Int): val fullName: String = s"$firstName $lastName" val people: Map[Int, Person] = Map( 1 -> Person("Andrea", "Thompson", 23), 2 -> Person("Austin", "May", 17), 3 -> Person("John", "Winston", 18), 4 -> Person("Christina", "Chandler", 20), 5 -> Person("John", "Thompson", 23) )
foreach メソッドの使い方
foreach
メソッドは、Map
クラスの要素それぞれについて副作用を発生させる際にいちばん基本となるメソッドでした。
過去形なのには理由があります。
後述の foreachEntry
メソッドについてを参照してください。
foreach
メソッドにおいては、それぞれの要素のキーや値は case
キーワードで抽出することになります。
people.foreach { case (_, person) => println(person.fullName) }
出力は以下のとおりです。
以降のサンプルではそれぞれ出力結果は同じになります。
John Thompson Andrea Thompson Austin May John Winston Christina Chandler
case
キーワードを使用しない場合にはタプルが渡ってきます。
タプルの形で欲しい場合はこれでOKです。
裏を返せば、キーや値など個別のデータを使用したい場合には、_1
や _2
などといった形で、タプルの変数から展開する必要があります。
この場合、書いた瞬間はよいのですが、後から見ると何のことやらわかりにくいですね。
一旦変数に格納するなど工夫する必要がありました。
people.foreach { tuple => println(tuple._2.fullName) }
そんなときに便利なのが foreachEntry
メソッドです。
foreachEntry メソッドの使い方
Scala 2.13 から、foreachEntry
メソッドが追加されました。
これは先程と同じような処理を case
キーワードによる抽出無しで実現してくれるメソッドです。
foreachEntry
メソッドを使用することにより、わずかではありますがコードの見通しが良くなります。
プログラムを書く上では、要素をタプルで欲しい場合よりもキーや値として分解された状態で使用したい場合のほうが多いため、
今後使用頻度の高くなる、基本のメソッドです。
最初に選択肢として挙がるように心がけつつ、必要に応じて foreach
メソッドと使い分けていきましょう。
people.foreachEntry((_, person) => println(person.fullName))
出力結果は上述の foreach
メソッドを実行したのと同じになります。
map メソッドの使い方
副作用を発生させつつ、後続の処理に値をそのまま渡したいことがあると思います。
その場合、これまでは map
メソッド内で副作用を発生させていました。
また過去形ですね。
つまり tapEach
メソッドを参照してください。
map
メソッドを使った書き方は以下のようになります。
map
メソッドについても foreach
メソッドと同様に、キーや値を抽出するには case
キーワードを使用する必要があります。
前の処理から渡ってきた要素を、またブロックの末尾で明示的に書くことで次の処理に渡しています。
people.map[Int, Person] { case (k, person) => println(person.fullName.toUpperCase) (k, person) }.foreachEntry((_, person) => println(person.fullName))
JOHN THOMPSON ANDREA THOMPSON AUSTIN MAY JOHN WINSTON CHRISTINA CHANDLER John Thompson Andrea Thompson Austin May John Winston Christina Chandler
case
キーワードを使用しない場合はタプルが渡ってくるので、 _1
や _2
を使って中身のキーや値を取得する必要があります。
タプルが欲しい場合と不要な場合とで臨機応変に使い分けるとよさそうです。
people.map[Int, Person] { tuple => println(tuple._2.fullName.toUpperCase) tuple }.foreachEntry((_, person) => println(person.fullName))
tapEach メソッドの使い方
Scala 2.13 から、tapEach
メソッドが追加されました。
これは先程と同じような場面において、後続の処理に明示的に要素を渡すことなく次の処理に要素を渡してくれるメソッドです。
map
メソッドの場合と異なり、 tapEach
メソッドの末尾には何も渡されていないことがわかります。
people.tapEach { case (_, person) => println(person.fullName.toUpperCase) } .foreachEntry((_, person) => println(person.fullName))
出力結果は上述の map
メソッドを実行したのと同じになります。
まとめ
foreach
、foreachEntry
、tapEach
、map
の使い分けは以下のようになります(再掲)。
いずれも副作用のある処理をする場合において、
処理結果を後続の処理では利用せず、かつ処理にキーと値のタプルは不要なときは foreachEntry
を使う。
処理にキーと値のタプルが必要となるときは foreach
を使う。
処理結果を後続の処理で使いたいときは map
を使う。
処理結果ではなく元々の要素を後続の処理に渡したいときは tapEach
を使う。
特に、2.13 から入った foreachEntry
メソッド、tapEach
メソッドをぜひ活用してみてください。