【问题标题】:Add auto incremented values to scala map for null values将自动递增的值添加到 scala 映射以获取空值
【发布时间】:2021-05-25 14:48:29
【问题描述】:

我有如下的 Scala 地图:

val mapData = Map(AA1 -> 1, AA2 -> 2, AA3 -> null, AA4 -> null)

我想从最高地图值自动增加值(在上面的地图中它是“2”)。

val maxValue = mapData.valuesIterator.max

我想遍历这张地图,如果值为null,则用从最高地图值开始的自动递增值替换该值(在上面的地图中为2)。结果应该如下所示。顺序并不重要。

Map(AA1 -> 1, AA2 -> 2, AA3 -> 3, AA4 -> 4)

我尝试使用mapData.map(),但我无法在映射操作期间保留递增的值。 Scala 中还有其他方法可以实现这一点吗?

【问题讨论】:

  • 如果你有val mapData = Map(AA1 -> 1, AA2 -> 2, AA3 -> null, AA4 -> null, AA5 -> 5)会发生什么?另外,您是说顺序不重要Map(AA1 -> 1, AA2 -> 2, AA3 -> 4, AA4 -> 3) 是否有效?如果没有,顺序很重要。
  • Yes "Map(AA1 -> 1, AA2 -> 2, AA3 -> 4, AA4 -> 3)" 是一个有效的响应。如果输入是“val mapData = Map(AA1 -> 1, AA2 -> 2, AA3 -> null, AA4 -> null, AA5 -> 5)”,那么结果可以是“Map(AA1 -> 1, AA2 - > 2, AA3 -> 6, AA4 -> 7, AA5 -> 5)"

标签: scala scala-collections


【解决方案1】:

首先,您提供的代码 sn-p 将无法编译,因为 Null 中的值和键未在引号中交换被视为字符串。我已将值替换为 Option:

val mapData: Map[String, Option[Int]] = Map("AA1" -> Some(1), "AA2" -> Some(2), "AA3" -> None, "AA4" -> None)

val max = mapData.values.flatten.max
val (result, _) = mapData.toList.foldLeft(Map.empty[String, Int] -> (max + 1)) {
  case ((accumulator, nextMax), (key, None)) =>
    ((accumulator + (key -> nextMax)), nextMax + 1)
  case ((accumulator, nextMax), (key, Some(value))) =>
    ((accumulator + (key -> value)), nextMax)
}

println(result)

将打印下一个结果:Map(AA1 -> 1, AA2 -> 2, AA3 -> 3, AA4 -> 4) Sctie 示例:https://scastie.scala-lang.org/ltmhh3tuQCWf2deaLboK9w

更新: 正如 @Tomer Shetah 在 cmets 部分中正确指出的那样 - max 不安全,因为 List[Int].empty.max 会抛出异常。替换为maxOption

val mapData: Map[String, Option[Int]] = Map("AA1" -> Some(1), "AA2" -> Some(2), "AA3" -> None, "AA4" -> None)

val max = mapData.values.flatten.maxOption.getOrElse(0) //safer with maxOption
val (result, _) = mapData.toList.foldLeft(Map.empty[String, Int] -> (max + 1)) {
  case ((accumulator, nextMax), (key, None)) =>
    ((accumulator + (key -> nextMax)), nextMax + 1)
  case ((accumulator, nextMax), (key, Some(value))) =>
    ((accumulator + (key -> value)), nextMax)
}

println(result)

斯卡蒂:https://scastie.scala-lang.org/iiSvahIZStyL1MDi0Glsvg

【讨论】:

  • 注意如果您在mapData 中包含"AA5""AA6""AA7",您的输出会发生什么。
  • @jwvh 对不起,请您详细说明一下 - 我添加了这些案例,但到目前为止无法弄清楚出了什么问题。问题作者虽然说Order is not important.
  • 而且,“顺序不重要” 是件好事,因为Map,根据定义,没有顺序。但是OP是什么意思?这是否意味着演示顺序不重要(即AA5->5, AA2->2,...等可以)?或者这是否意味着递增的顺序不重要(即AA3->7, AA5->4,...等)?要求不明确。
  • @Mohan 你能详细说明一下要求吗?
  • @jwvh 非常感谢您提供详细信息。是的,我同意要求不明确,因此我尝试根据一些假设准备答案。
【解决方案2】:

这是一种基本方法,但可能不是优化的方法。

var mapData = Map("AA1" -> 1, "AA2" -> 2, "AA3" -> null, "AA4" -> null)

 val nonNull = mapData.values.filter(_ != null)
 val max = nonNull.map(_.toString.toInt).max
 var nullCount = mapData.count(_._2==null)

 mapData=mapData.map(t => {
 if (t._2 == null) {
    val output = (t._1, max + nullCount)
    nullCount -= 1
    output
}
else (t._1, t._2)
})
println(mapData)
   
//output : Map(AA1 -> 1, AA2 -> 2, AA3 -> 4, AA4 -> 3)

【讨论】:

    【解决方案3】:

    您的另一个选择是:

    val startFrom = mapData.values.filter(_.isInstanceOf[Int]).map(_.asInstanceOf[Int]).maxOption.getOrElse(0) + 1
    val values = LazyList.from(startFrom).iterator
    val result = mapData.view.mapValues {
      case x: Int => x
      case null => values.next()
      case _ => ???
    }
    

    请注意,result 未实现。您需要调用.toMap 来实现它。

    代码运行在Scastie

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-11-01
      • 2023-03-03
      • 1970-01-01
      • 1970-01-01
      • 2017-05-14
      • 2018-09-25
      • 1970-01-01
      相关资源
      最近更新 更多