Lambdaカクテル

京都在住Webエンジニアの日記です

Invite link for Scalaわいわいランド

Scalaで複数のリストをzipするならlazyZipがおすすめ(きれいに収まる)

Scalaでコードを書いていて、複数のリストをzipすることがあった。

val xs: List[Int] = ???
val ys: List[Int] = ???
val zs: List[String] = ???

xs.zip(ys).zip(zs) // => List[((Int, Int), String)]

しかしzipは2つのリストからList[(A, B)]を作るので、複数回zipすると奇妙な型になってしまう。この場合はList[((Int, Int), String)]になってしまった。こういう型になるとmapなどで使うときに面倒だ。

lazyZip

コレクションメソッドを探していると、lazyZipメソッドというのがあることを知った(Scala 2.13から導入されたようだ)。使い方はzipと同じだが、mapなどを使うタイミングにまでタプルを作るのを遅延させてくれる。

xs.lazyZip(ys).lazyZip(zs).map { (x, y, z) => /* (Int, Int, String)のタプルになっている */ }

これは非常に便利だ。

注意点として、自動でタプルを潰してくれるのは.lazyZipのチェインが3回を超えない場合のみだ。.lazyZipを4度以上呼び出すと、zipのときと同じように((A, B, C, D), E)の形になってしまう。

zipped

ちなみに一度タプルを作成してからzippedメソッド(Scala 2.12で登場)を呼ぶというテクニックもあるが、これはdeprecatedになっていて将来削除されるかもしれないので非推奨だ。

(xs, ys, zs).zipped // => List[(Int, Int, String)]

Catsを使う場合

関数型ライブラリであるCatsを使うと、複数のリストを柔軟にzipできるし、個数の制限がより広い。

import cats._
import cats.implicits._

val xs = List(1,2,3,4,5)
val ys = List(2,3,4,5,6)
val zs = List("a","b","c","d","e")

(xs, ys, zs).parMapN((x, y, z) => (x, y, z)) // => List((1,2,a), (2,3,b), (3,4,c), (4,5,d), (5,6,e))

blog.3qe.us

parMapNはそれだけで記事が1つ書けるので、いったんこういうものとして覚えてほしい。

Scala 3からはタプルの個数は無制限になっているので、いくつでも動くはずだ。

scalapedia.com

★記事をRTしてもらえると喜びます
Webアプリケーション開発関連の記事を投稿しています.読者になってみませんか?