bugfix> scala > 投稿

同じタイトル(この場合は私のキー)を持つScalaの要素の合計を計算する問題に直面しています。

現在、私の入力は次のように説明できます。

val listInput1 = 
  List(
    "itemA,CATA,2,4 ",
    "itemA,CATA,3,1 ",
    "itemB,CATB,4,5",
    "itemB,CATB,4,6"
   )
val listInput2 = 
  List(
    "itemA,CATA,2,4 ",
    "itemB,CATB,4,5",
    "itemC,CATC,1,2"
  )

入力のリストに必要な出力は

val listoutput1 = 
  List(
    "itemA,CATA,5,5 ",
    "itemB,CATB,8,11"
  )
val listoutput2 =
  List(
    "itemA , CATA, 2,4 ",
    "itemB,CATB,4,5",
    "itemC,CATC,1,2"
  )

次の関数を作成しました。

def sumByTitle(listInput: List[String]): List[String] =      
  listInput.map(_.split(",")).groupBy(_(0)).map { 
    case (title, features) => 
       "%s,%s,%d,%d".format(
         title,
         features.head.apply(1),
         features.map(_(2).toInt).sum,
         features.map(_(3).toInt).sum)}.toList

行の順序が変わるため、期待した結果が得られません。

どうすれば修正できますか?

回答 3 件
  • ザ・ ListMap   Map に挿入されたアイテムの順序を保持するように設計されています 。

    import collection.immutable.ListMap
    def sumByTitle(listInput: List[String]): List[String] = {
      val itemPttrn = raw"(.*)(\d+),(\d+)\s*".r
      listInput.foldLeft(ListMap.empty[String, (Int,Int)].withDefaultValue((0,0))) {
        case (lm, str) =>
          val itemPttrn(k, a, b) = str  //unsafe
          val (x, y) = lm(k)
          lm.updated(k, (a.toInt + x, b.toInt + y))
      }.toList.map { case (k, (a, b)) => s"$k$a,$b" }
    }
    
    

    これは、入力文字列が正規表現パターンと一致しない場合にスローされるため、少し安全ではありません。

    sumByTitle(listInput1)
    //res0: List[String] = List(itemA,CATA,5,5, itemB,CATB,8,11)
    sumByTitle(listInput2)
    //res1: List[String] = List(itemA,CATA,2,4, itemB,CATB,4,5, itemC,CATC,1,2)
    
    

    末尾のスペースがある場合、それは保持されないことに注意してください。

  • 並べ替えだけに興味がある場合は、単に sorted を返すことができます  リスト:

    val listInput1 = 
      List(
        "itemA , CATA, 2,4 ",
        "itemA , CATA, 3,1 ",
        "itemB,CATB,4,5",
        "itemB,CATB,4,6"
       )
    val listInput2 = 
      List(
        "itemA , CATA, 2,4 ",
        "itemB,CATB,4,5",
        "itemC,CATC,1,2"
      )
    def sumByTitle(listInput: List[String]): List[String] =      
      listInput.map(_.split(",")).groupBy(_(0)).map { 
        case (title, features) => 
           "%s,%s,%d,%d".format(
             title,
             features.head.apply(1),
             features.map(_(2).trim.toInt).sum,
             features.map(_(3).trim.toInt).sum)}.toList.sorted
    println("LIST 1")
    sumByTitle(listInput1).foreach(println)
    println("LIST 2")
    sumByTitle(listInput2).foreach(println)
    
    

    Scastieでコードを見つけて、遊んでみてください。


    サイドノートとして、あなたはあなたのビジネスロジックからシリアライゼーションとデシリアライゼーションを分離することに興味があるかもしれません。

    ここでは、懸念を分離するための第一歩として、比較的素朴なアプローチを備えた別のScastieノートブックを見つけることができます。

  • def foldByTitle(listInput: List[String]): List[Item] =
      listInput.map(Item.parseItem).foldLeft(List.empty[Item])(sumByTitle)
    val sumByTitle: (List[Item], Item) => List[Item] = (acc, curr) =>
      acc.find(_.name == curr.name).fold(curr :: acc) { i =>
        acc.filterNot(_.name == curr.name) :+ i.copy(num1 = i.num1 + curr.num1, num2 = i.num2 + curr.num2)
      }
    case class Item(name: String, category: String, num1: Int, num2: Int)
    object Item {
      def parseItem(serializedItem: String): Item = {
        val itemTokens = serializedItem.split(",").map(_.trim)
        Item(itemTokens.head, itemTokens(1), itemTokens(2).toInt, itemTokens(3).toInt)
      }
    }
    
    

    これにより、要素の初期順序が保持されます。

あなたの答え