1. 序章

ScalaのMapは、キーと値のペアとモデル辞書を格納するための強力なデータ構造です。 マップ内のキーは一意であり、対応する値に直接アクセスできます。さらに、標準ライブラリには、キーと値の両方をマップするいくつかの方法があります

このチュートリアルでは、キーと値に変換を適用するいくつかの方法を見ていきます。

2. マッピング関数

Scala Map [K、V] のキーと値の両方を変換する主な方法は3つあります。ここで、 K はキーのタイプであり、Vは値のそれ。 これらのメソッドは、 Map :: map Map :: flatMap 、および Map ::transformです。

2.1. Map :: map

まず、一般的なmap関数を見てみましょう。

def map[B](f: ((K, V)) => B): Iterable[B]

Map :: mapは、キーと値のペアとして表示されるすべての要素に関数fを適用することにより、新しいIterableを構築します。その署名からわかるように、fは単一の値を返しますキーと値のペアごとにタイプBの。 したがって、 Map ::mapの戻り値はIterable[B]になります。 例を見てみましょう:

val m = Map(1 -> "A", 2 -> "B")
val f = { t: (Int, String) => s"${t._1}${t._2}" }
(m map f) shouldBe Iterable("1A", "2B")

上記の例では、 Map [Int、String] から始めて、タイプ((Int、String))=>Stringの関数fを定義しました。つまり、ペア(Int、String)からStringへの関数です。 パラメータtがタプルであるという事実は、その要素にアクセスする方法を見ると明らかです。Mapおよびのキーのt._1対応する値については]t._2。 キーと値をマップすると、String型の要素を持つ新しいIterableを取得します。

fがバイナリ関数の場合、Map::mapにすぐに提供することはできません。 この場合、代わりにf.tupledを使用する必要があります :

val m = Map(1 -> "A", 2 -> "B")
val f = { (k: Int, v: String) => s"$k$v" }
(m map f.tupled) shouldBe Iterable("1A", "2B")

f.tupled は、バイナリ関数を、入力がペアである単項関数に変換します。

Map::mapはオーバーロードされたメソッドです。 実際のところ、戻り値にマップタイプを保持する別のバージョンが存在します :

def map[K2, V2](f: ((K, V)) => (K2, V2)): Map[K2, V2]

上記の関数は前に見たものと似ていますが、重要な違いが1つあります。マッピング関数fは、単一の値ではなく新しいペア(K2、V2)を返します。したがって、戻り型は次のようになります。新しいマップ[K2、V2] になります:

val m: Map[Int, String] = Map(1 -> "A", 2 -> "BB")
val newMap: Map[String, Int] = m map { 
  case (k, v) => (k.toString, v.length)
}
newMap shouldBe Map("1" -> 1, "2" -> 2)

上記の例では、Scalaのシンタックスシュガーを活用し、パターンマッチングを使用してタプルを破棄しました。 次の例では、上記のようにマッピング関数を指定する代わりに、このスタイルを引き続き使用します。

最後の例の主な違いは、mに適用されてnewMapを取得する関数が新しいペアを返すことです。ここで、キーは元のペアから計算されます(数値を String )と対応する値は、元の値の長さをカウントすることによって取得されます。

したがって、最後のアサーションでは、一般的な Iterableの代わりに、タイプとして Mapを指定できます。さらに、mとnewMapの明示的なタイプは、2つの値を明確にします。どちらもマップです。

マッピング関数が同じキーを持つ2つ(またはそれ以上)のペアを返す場合、最後の1つだけが結果のマップに追加されます。これは、上記で見たように、マップのキーが原因です。 は一意である必要があります:

val m: Map[Int, String] = Map(1 -> "A", 2 -> "BB")
val newMap: Map[Int, Int] = m map { case (_, v) => (1, v.length) }
newMap shouldBe Map(1 -> 2)

2.2. Map :: flatMap

キーと値の両方をマップする2番目の方法はMap::flatMapです。他のScalaコレクションと同様に、 flatMap は、マッピング関数によって返されるコレクションを単一のフラットなものにフラット化します。 以前と同様に、 Map :: flatMap には2つのフレーバーがあります。これは、マッピング関数が単一の要素またはペアを返すことができるためです。

def flatMap[K2, V2](f: ((K, V)) => IterableOnce[(K2, V2)]): Map[K2, V2]
def flatMap[B](f: ((K, V)) => IterableOnce[B]): Iterable[B]

2つのシグニチャによると、 Map :: map に関する唯一の違いは、関数fIterableOnceを返すことです。 Map ::flatMapを実際に使用する方法を見てみましょう。

val m = Map(1 -> "A", 2 -> "B", 3 -> "C")

val newIterable: Iterable[String] = m flatMap {
  case (k, v) => List.fill(k)(v)
}
newIterable shouldBe Iterable("A", "B", "B", "C", "C", "C")

val newMap: Map[Int, String] = m flatMap {
  case (k, v) => (1 to k).map(i => i -> s"$i$v")
}
newMap shouldBe Map(1 -> "C", 2 -> "CC", 3 -> "CCC")

上記の例の最初の部分では、Map ::flatMapを使用してIterable[String]の新しいインスタンスを作成します。特に、キーと値のペアごとに、Listを作成します。 ]キーで表されるペアの値を何度も繰り返すことによって。 いつものように、 flatMap は、結果のコレクションを Iterable( “A”、 “B”、 “B”、 “C”、 “C”、 “C”)にフラット化します。

例の2番目の部分では、Map :: flatMapを使用して新しいMap[Int、String]を作成します。この場合、変換関数はもう少し複雑です。

基本的に、元のマップの各キーの値として k が与えられると、kの新しいペアが返されます。 各ペアの最初の要素は増加するインデックスですから 1 に k。 代わりに、2番目の要素は、インデックスを持つ元のペアの値です。プレフィックスとして。 たとえば、元のマップの2番目のペア( 2-> “B” )に適用された変換関数は、 Iterable(1-> “1B”、2-> “2B”)を生成します。

Map :: flatMapは、重複するペアの削除も処理します。したがって、最終結果は Map(1->“ 1C”、2->“ 2C”、3->“ 3C”になります。 )。 これは、ペア(1->“ A”および2->“ B”)で、最初の2回の反復によって生成されたキーが、ペア3->” C”

2.3. Map :: transform

Scalaマップでキーと値をマップする別の方法はMap::transformです。 与えられたマップ[K、V] 、それは私たちが構築することを可能にしますマップ[K、W] 、ここで(タイプの)新しい値を生成できます W )元の各要素のキーと値の両方を考慮に入れることによって地図

def transform[W](f: (K, V) => W): Map[K, W]

これは、変換関数が各要素のキーにもアクセスできるため、マップの値をマッピングするだけとは異なります

val m: Map[Int, Char] = Map(1 -> 'A', 2 -> 'B', 3 -> 'C')

val newMap: Map[Int, String] = m transform {
  case (k, v) => s"$k$v"
}
newMap shouldBe Map(1 -> "1A", 2 -> "2B", 3 -> "3C")

上記の例では、Map [Int、Char]から開始し、元のキーをそのままにして新しいマップを作成します。ただし、キーを元の値の前に追加します。新しいマップ。 この目的のためにMap::transformを使用できます。 結果は、予想どおり、 Map [Int、String]です。

3. 結論

この記事では、Scala Mapのキーと値の両方をマップする方法を見ました。 特に、 Map :: map Map :: flatMap 、および Map ::transformの3つの異なる方法を分析しました。 さらに、mapflatMapの2つのオーバーロードを調査し、それらのシグネチャを比較しました。

いつものように、サンプルコードはGitHubから入手できます。