【问题标题】:How to partially reduce list in scala如何部分减少scala中的列表
【发布时间】:2020-04-12 08:37:53
【问题描述】:

我有一个整数列表(代表手牌值)

val values = List(1, 200, 3, 4, 45, 45, 7, 8)

以及进入这个列表的索引列表(代表参与底池的手牌)

val indexes = List(0, 4, 5, 7)

我需要过滤索引,以便只保留手牌值最高的那些,将包含相同的最高手牌值。

在这种情况下,输出将是 List(4, 5),因为 4 和 5 的最高手牌值均为 45。

【问题讨论】:

    标签: list scala functional-programming scala-collections


    【解决方案1】:

    我会分几个步骤来做:

    val tuples = indexes.map(index => index -> values(index)) // mapping index - value
    val maxVal = tuples.map(_._2).max // get max value
    val maxIndices = tuples.filter(t => t._2 == maxVal).map(_._1) // filter & map tuples
    

    为简洁起见,我对元组使用了位置表示法(_1_2),这当然可以使用模式匹配来编写,以使其更具 scala-ish。

    P.S.1:感谢 @Joe K 简化聚合。

    P.S.2:@Luis Miguel Mejía Suárez 注意到,最后一行可以通过模式匹配压缩成一个 collect 方法:

    tuples.collect { case (i, v) if (v == maxVal) => i }
    

    【讨论】:

    • 我以为 maxVal 会返回 200 但它返回 45 对吗?
    • 是的,indexes 已映射,因此只考虑那些
    • 我觉得第二行可以简化:val maxVal = tuples.map(_._2).max
    • @LuisMiguelMejíaSuárez 非常感谢你,我希望没关系,我在回答中用你的名字添加了这个;>
    • 其实那个collect语句可以进一步细化:tuples.collect{case (i,`maxVal`) => i}
    【解决方案2】:
    val indexedValues = values.view.zipWithIndex
    val maxValue = indexedValues.toList.filter{case (v,i) => indexes.contains(i)}.maxBy(_._1)._1
    indexedValues.filter{ case (v,i) => v==maxValue }.map(_._2).toList
    

    在 Scala REPL 中:

    Welcome to Scala 2.13.1 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_231).
    Type in expressions for evaluation. Or try :help.
    
    scala> val values = List(1, 200, 3, 4, 45, 45, 7, 8)
    values: List[Int] = List(1, 200, 3, 4, 45, 45, 7, 8)
    
    scala> val indexes = List(0, 4, 5, 7)
    indexes: List[Int] = List(0, 4, 5, 7)
    
    scala> val indexedValues = values.view.zipWithIndex
    indexedValues: scala.collection.View[(Int, Int)] = View(<not computed>)
    
    scala> val maxValue = indexedValues.toList.filter{case (v,i) => indexes.contains(i)}.maxBy(_._1)._1
    maxValue: Int = 45
    
    scala> indexedValues.filter{ case (v,i) => v==maxValue }.map(_._2).toList
    res0: List[Int] = List(4, 5)
    

    【讨论】:

    • filter + map === collect - 因此indexedValues.collect { case (v, i)) if (v == maxValue) =&gt; i }.toList
    【解决方案3】:

    恕我直言,以自定义方式处理列表的最佳方法是编写尾递归算法。

    def filterHighestIndexes(indexes: Set[Int])(data: List[Int]): List[Int] = {
      @annotation.tailrec
      def loop(remaining: List[Int], acc: List[Int], currentMax: Int, currentIdx: Int): List[Int] =
        remaining match {
          case x :: xs =>
            val (newAcc, newMax) = 
              if (!indexes.contains(currentIdx) || x < currentMax)
                acc -> currentMax
              else if(x == currentMax)
                (currentIdx :: acc) -> currentMax
              else
                List(currentIdx)  -> x
    
            loop(
              remaining = xs,
              newAcc,
              newMax,
              currentIdx + 1
            )
    
          case Nil =>
            acc.reverse
        }
    
      data match {
        case x :: xs =>
          loop(
            remaining = xs,
            acc = List(0),
            currentMax = x,
            currentIdx = 1
          )
    
        case Nil =>
          List.empty
      }
    }
    

    你可以像这样测试它

    val values = List(1, 200, 3, 4, 45, 45, 7, 8)
    val indexes = Set(0, 4, 5, 7)
    
    filterHighestIndexes(indexes)(values)
    // res: List[Int] = List(4, 5)
    

    注意,我使用 Set 而不是 List 作为索引只是为了使contains 更有效。如果您无法更改,只需使用 List 即可。

    【讨论】:

      【解决方案4】:

      我觉得这个就这么简单:

      scala> indexes.groupBy(values(_)).maxBy(_._1)._2
      res6: List[Int] = List(4, 5)
      

      【讨论】:

        猜你喜欢
        • 2021-12-02
        • 2018-11-11
        • 2015-11-30
        • 1970-01-01
        • 1970-01-01
        • 2011-11-04
        • 2018-04-24
        • 1970-01-01
        • 2020-11-14
        相关资源
        最近更新 更多