【Scala】Scala ~ コレクションで使えるメソッド ~

■ はじめに

https://dk521123.hatenablog.com/entry/2023/03/13/000345

の続き。

Scala を勉強してて、コレクションの操作がちゃんと分かっていないと
話にならなそうなので、基本事項をまとめる

目次

【00】コレクション変換のコツ
【01】foreach / zipWithIndex
【02】map
【03】collect
【04】find
【05】takeWhile
【06】dropWhile
【07】span
【08】filter / filterNot
【09】partition
【10】groupBy
【11】forall
【12】exists
【13】count
【14】flatten / flatMap

【00】コレクション変換のコツ

メソッド 説明
map 元の要素と変換先の要素が1:1に対応する時
flatMap 元の要素一つに変換先の要素が0からn個に対応する時
filter/filterNot 元の要素の内、条件に合致する/しないものを取り除きたい時
take/takeWhile/dropRight 元のコレクションの前方からいくつかの要素を取り出したい時
drop/dropWhile/takeRight 元のコレクションの後方からいくつかの要素を取り出したい時
++演算子/:::演算子 2つのコレクションを繋ぎ合わせたい時

【01】foreach / zipWithIndex

* 繰り返し
 => Mapの場合、タプルで返され、Keyは、x._1、Valueは、x._2 となる
* zipWithIndex は、インデックス付き(例3参照)

https://docs.scala-lang.org/scala3/book/collections-methods.html#foreach

サンプル

例1:Array with foreach

object  Hello {
  def main(args: Array[String]): Unit = {
    val nameList = Array("Mike", "Tom", "Sam")
    
    //nameList.foreach(println)
    //nameList.foreach(println _)
    //nameList.foreach(println(_))
    //nameList.foreach((x: Any) => println(x))
    nameList.foreach(x => println(x))
  }
}

例2:Map with foreach

val sampleMap = Map("x1" -> "Mike", "x2" -> "Tom")

// for
for ((k,v) <- sampleMap) print(s"key: $k, value: $v / ")
println("\n**********")

// foreach => Mapの場合、タプルで返され、Keyは、x._1、Valueは、x._2 となる
sampleMap.foreach(pair => print(s"key: ${pair._1}, value: ${pair._2} / "))
println("\n**********")

sampleMap.foreach { case (key, value) =>
  if (value == "Tom") {
    print(s"key: $key, value: $value")
  }
}
println("\n**********")

例3:Map with foreach - 別解: key/value
https://dk521123.hatenablog.com/entry/2024/07/23/193246

// より抜粋
    mapper.foreach { case (key: String, value: ConfigType) =>
      println(s"${key} - ${toPrintStr(value)}")
    }

https://stackoverflow.com/questions/8610776/scala-map-foreach

例4:zipWithIndex

object  Hello {
  def main(args: Array[String]): Unit = {
    val sampleList = Array("x1", "x2", "x3")
    sampleList.zipWithIndex.foreach {
      case(x: String, i:Int) => println(i, x)
    }

    val sampleMap = Map("x1" -> "Mike", "x2" -> "Tom")
    sampleMap.zipWithIndex.foreach {
      case(x: (String, String), i:Int) => println(i, x._1, x._2)
    }
  }
}

https://alvinalexander.com/scala/create-iterating-scala-string-maps/
https://www.baeldung.com/scala/iterate-map

【02】map

* 要素を加工する

https://docs.scala-lang.org/ja/overviews/collections/maps.html

サンプル

例1:For List

import scala.io.Source
import java.nio.file.{Paths, Files}
import java.nio.charset.StandardCharsets

object  Hello {
  def main(args: Array[String]): Unit = {
    val nameList = Array("Mike", "Tom", "Sam")

    //val result1s = nameList.map(_.toLowerCase())
    //val result2s = nameList.map((x) => x.toLowerCase())
    //val result3s = nameList.map(x => x.toLowerCase())
    val results = nameList.map((x: String) => x.toLowerCase())
    results.foreach(println)
  }
}

例2:For Map, To convert Map to another Map

object Hello {
  def main(args: Array[String]): Unit = {
    val mapper = Map("A" -> "1", "B" -> "2", "C" -> "3")

    // To convert Map[String, String] to another map - Map[String, Int]
    val anotherMapper: Map[String, Int] = mapper.map{ case(key, value) =>
      (key, value.toInt)
    }

    // Output
    anotherMapper.foreach {
      case(key, value) => println(s"${key} - ${value}")
    }
  }
}

【03】collect

* マッチした要素を書こう

サンプル

import scala.io.Source
import java.nio.file.{Paths, Files}
import java.nio.charset.StandardCharsets

object  Hello {
  def main(args: Array[String]): Unit = {
    val nameList = Array("Mike", "Tom", "Sam")

    val results = nameList.collect {
      case "Mike" => "Hi, Mike"
      case x => x
    }
    results.foreach(println)
  }
}

【04】find

* 見つけた要素を返す

サンプル

import scala.io.Source
import java.nio.file.{Paths, Files}
import java.nio.charset.StandardCharsets

object  Hello {
  def main(args: Array[String]): Unit = {
    val numberList = Array(1, 2, 3, 4, 5)

    // 最初に条件は true になったものを Some で返す(なければ None)
    println(numberList.find(x => x % 2 != 0)) // Some(1)
    println(numberList.find(x => x % 2 == 0)) // Some(2)
    println(numberList.find(_ <= 0)) // None
  }
}

【05】takeWhile

* 先頭から条件を満たしている間を抽出する

https://scala-text.github.io/scala_text/collection.html
https://qiita.com/suzuki-hoge/items/7cf2e4989a4e860e923f

サンプル

object  Hello {
  def main(args: Array[String]): Unit = {
    val numberList = Array(1, 2, 3, 4, 5, 4, 3, 2, 1)

    val results = numberList.takeWhile(_ < 4)
    println(results.mkString(", ")) // 1, 2, 3
  }
}

【06】dropWhile

* 先頭から条件を満たしている間を除外する

サンプル

object  Hello {
  def main(args: Array[String]): Unit = {
    val numberList = Array(1, 2, 3, 4, 5, 4, 3, 2, 1)

    val results = numberList.dropWhile(_ < 4)
    println(results.mkString(", ")) // 4, 5, 4, 3, 2, 1
  }
}

【07】span

*  先頭から条件を満たしている間の要素を2分割する

https://www.baeldung.com/scala/split-sequence

サンプル

object  Hello {
  def main(args: Array[String]): Unit = {
    val numberList = Array(1, 2, 3, 4, 5, 4, 3, 2, 1)

    val (result1s, result2s) = numberList.span(_ < 4)
    println(result1s.mkString(",")) // 1,2,3
    println(result2s.mkString(",")) // 4,5,4,3,2,1
  }
}

【08】filter / filterNot

* 条件に一致・不一致した要素を抽出する

https://stackoverflow.com/questions/27942480/using-regex-with-filter-in-scala

サンプル

例1:空文字を除外する

object Hello {
  def main(args: Array[String]): Unit = {
    println("Start")
    val targetList = List("Start", "", "Hello", "", "World", "End")
    val results = targetList.filter(x => x.nonEmpty)
    println(s"Done. ${results.mkString(",")}") // Done. Start,Hello,World,End
  }
}

例2:正規表現でフィルタリングする

object  Hello {
  def main(args: Array[String]) {
    println("Start")
    val targetList = Seq("a12345", "b123456", "aaaa1111", "123456", "xxxxxxx")
    // 【文字列】【6桁数字】
    val regex = """^([a-zA-Z]\d{6})""".r
    val results = targetList.filter {
      x => regex.findFirstIn(x).isDefined
    }
    println(s"Done. ${results}") // List(b123456)
  }
}

【09】partition

* 条件で2分割する

サンプル

object  Hello {
  def main(args: Array[String]): Unit = {
    val numberList = Array(1, 2, 3, 4, 5, 4, 3, 2, 1)

    val (result1s, result2s) = numberList.partition(_ < 4)
    println(result1s.mkString(",")) // 1,2,3,3,2,1
    println(result2s.mkString(",")) // 4,5,4
  }
}

【10】groupBy

* 要素を分類する

サンプル

object  Hello {
  def main(args: Array[String]): Unit = {
    val numberList = Array(1, 2, 3, 4, 5, 4, 3, 2, 1)

    // Map[] で値を返す
    val results = numberList.groupBy(_ < 4)
    for ((key, values: Array[Int]) <- results) {
      val value = values.mkString(",")
      println(s"${key}: ${value}")
    }
  }
}

出力結果

true: 1,2,3,3,2,1
false: 4,5,4

【11】forall

* 要素全体をチェックし、
 全ての要素が合致していれば、true(それ以外は false)を返す

サンプル

object  Hello {
  def main(args: Array[String]): Unit = {
    val numberList = Array(1, 2, 3, 4, 5, 4, 3, 2, 1)

    // 全ての要素が合致していれば、true
    val isTrue = numberList.forall(_ > 0)
    println(s"result = ${isTrue}") // result = true

    // 全ての要素が合致していないので、false
    val isFalse = numberList.forall(_ > 1)
    println(s"result = ${isFalse}") // result = false
  }
}

【12】exists

* 合致する要素の存在をチェックし、
 要素が存在すれば、true(それ以外は false)を返す

サンプル

object  Hello {
  def main(args: Array[String]): Unit = {
    val numberList = Array(1, 2, 3, 4, 5, 4, 3, 2, 1)

    // 要素が存在すれば、true
    val isTrue = numberList.exists(_ != 5)
    println(s"result = ${isTrue}") // result = true

    // 要素が存在していないので、false
    val isFalse = numberList.exists(_ > 8)
    println(s"result = ${isFalse}") // result = false
  }
}

【13】count

* 合致する要素をカウントする

サンプル

object  Hello {
  def main(args: Array[String]): Unit = {
    val numberList = Array(1, 2, 3, 4, 5, 4, 3, 2, 1)

    val resultFor3 = numberList.count(_ == 3)
    println(s"result = ${resultFor3}") // result = 2

    val resultForOver8 = numberList.count(_ > 8)
    println(s"result = ${resultForOver8}") // result = 0
  }
}

【14】flatten / flatMap

flatten

* コレクションの要素がネストしている場合に
 平坦(Flat)にするために使用

flatMap

* mapしてから、flattenする
 

参考文献

https://qiita.com/f81@github/items/62ad3c56ce6be271ef35

関連記事

Scala ~ 環境構築編 ~
https://dk521123.hatenablog.com/entry/2023/03/10/193805
Scala ~ 入門編 ~
https://dk521123.hatenablog.com/entry/2023/03/12/184331
Scala ~ 基本編 / コレクション ~
https://dk521123.hatenablog.com/entry/2023/03/13/000345
Scala ~ mutable collection ~
https://dk521123.hatenablog.com/entry/2024/07/09/223730
Scala ~ 部分関数 / PartialFunction ~
https://dk521123.hatenablog.com/entry/2023/09/08/235546
Scala ~ 代数的データ型 / ADT ~
https://dk521123.hatenablog.com/entry/2024/07/23/193246