Scala で List から Mapに変換する

2022年6月5日 engineering

こんにちは、 @kz_morita です。

Scala で List から Map に変換する方法についてまとめます。

toMap で Map にする

list から Map にするためには、まず List を Tuple のリストに変換してから toMap メソッドを呼ぶことで変換することができます。

val list = Seq(1,2,3,4,5)
list.map(i => (i, i.toString)).toMap // Map<Int, String>

実装の中身をみる

toMap の定義を見ると以下のようになっています。

def toMap[K, V](implicit ev: A <:< (K, V)): immutable.Map[K, V] =
  immutable.Map.from(this.asInstanceOf[IterableOnce[(K, V)]])

Map.from の定義を見ると以下のようになってました。

def from[K, V](it: collection.IterableOnce[(K, V)]): Map[K, V] =
  it match {
    case it: Iterable[_] if it.isEmpty => empty[K, V]
    case m: Map[K, V] => m
    case _: (newBuilder[K, V] ++= it).result()
  }

Map.from では IterableOnce 型を受け取って、Mapに追加してます。

toMap は IterableOnce 型に生えているメソッドで以下の implicit の条件を満たさないと呼ぶことができません。

(implicit ev: A <:< (K, V))

気になるのが、<:< です。

定義を見ると以下のようになっています。

/**
  * An instance of `A <:< B` witnesses that `A` is a subtype of `B`.
  * 省略...
  */
 // They are here simply for reference as the "correct", safe implementations.
 @implicitNotFound(msg = "Cannot prove that ${From} <:< ${To}.")
 sealed abstract class <:<[-From, +To] extends (From => To) with Serializable {
 }

A が B のサブタイプであることを示す型のようです。

おおもとの implicit の型を見ると

A <:< (K, V)

となっているので、IterableOnce の型 A が、Tuple (K, V) のサブタイプであることを指定してます。

つまり、toMap を呼ぶためには、(K, V) のサブタイプ型の IterableOnce である必要がありそうです。

ちなみに、

@implictNotFound アノテーションで implicit が定義されていないときのメッセージが指定されているようでした。

実際に以下のようなコードを書くと

val list = Seq(1,2,3,4,5)
list.toMap

以下のようなエラーメッセージが表示されました。

Cannot prove that Int <:< (K, V)

where:    K is a type variable
          V is a type variable
.

まとめ

今回は、List から Map に変換する方法についてまとめました。 実装の中身を見ると、implicit などを用いてうまく書かれてるなと参考になることが多いです。IntelliJ などIDE を使っていれば簡単にソースも見れて、面白いのでおすすめです。

この記事をシェア