【问题标题】:Options in Scala with Boolean default value with getOrElse带有布尔默认值的 Scala 中带有 getOrElse 的选项
【发布时间】:2020-04-14 23:26:42
【问题描述】:

我有一个包含一些对的映射,我想为一个键分配一个默认值(布尔值),当我使用 getOrElse 时,一个键的默认值是一个字符串,我正在寻找一种方法来做到这一点一个整数或布尔值。

例如:

val someMap= Map("key1"->"value1", "key2"->3, "key3"-> true)
//let's see if I want to get the key `key4` with a default value of false using getOrElse
val key4= someMap.getOrElse("key4", false) //which I couldn't achieve.

【问题讨论】:

  • 我用 Scala 2.13 运行了你的代码,它编译并运行没有任何问题,产生了 key4 :Any = false 结果。
  • 是的,当我在控制台中运行时,我得到了同样的结果,但是当我在我的代码中尝试时,它给了我这个。:错误:类型不匹配; [ERROR] found : Any [ERROR] required: Boolean
  • val someMapMap[String,Any] 类型,因此不能在任何需要 Boolean 的上下文中使用。
  • 您始终可以通过添加.asInstanceOf[Boolean] 将结果转换为布尔值 - 根据您的用例,这可能不是一个好主意。
  • 一开始就不应该有 Any。那是代码的味道。为什么需要Map[String, Any] 普通类(或案例类) 不能更好地工作?或者,您可能正在尝试对 JSON 之类的东西进行建模?

标签: scala scala-option


【解决方案1】:

这是因为 someMap 的类型是 Map[String, Any]。我建议实现自定义方法

implicit class SettingsMapOps[K](anyMap: Map[K, _]) {
  def isEnabled(key: K): Boolean = anyMap.get(key).map {
    case b: Boolean => b
    case _ => false
  }.getOrElse(false)
}

因此,鉴于您的示例,代码 someMap.isEnabled("key3") 将返回 true,而其他所有代码均为 false。

【讨论】:

    【解决方案2】:

    让我们更严格一点。其中一个选项是使用 sum 类型(又名 coproduct)来类型安全地定义地图中的内容:

    sealed trait Value
    case class StringValue(value: String) extends Value
    case class IntValue(value: Int) extends Value
    case class BooleanValue(value: Boolean) extends Value
    val someMap: Map[String,Value] = Map("key1"->StringValue("value1"), "key2"->IntValue(3), "key3"-> BooleanValue(true))
    val key4= someMap.getOrElse("key4", BooleanValue(false))
    

    附:为了避免这些有点丑陋的案例类,你可以使用scalaz.Coproduct 甚至像iota 这样的东西。

    【讨论】:

      【解决方案3】:

      它在 scalafiddle 的 scala 2.12 上运行良好,不知道为什么它在你的情况下不起作用: https://scalafiddle.io/sf/uzZNFH5/1

      此外,您可以使用withDefaultValue 来简化您的地图(它总是会为不存在的键返回指定的值)

      val someMap:Map[String, Any] = Map("key1"->"value1", "key2"->3, "key3"-> true).withDefaultValue(false)
      

      您还可以使用withDefault,它使您可以将不存在的键映射到预期值。

      但我仍然同意其他人的观点,即首先使用 Map[String, Any] 并不是完美的解决方案:(。

      已编辑

      好的...从 cmets 我看到您的问题是您不能使用返回值,因为它是 Any 不是布尔值。你可以这样做:

      //first option
      someMap.get("key4").map {
        case x:Boolean => x
        case _ => true //or whatever should be taken when answer is string
      }.getOrElse(false)
      
      //second option
      val someMapV:Map[String, Boolean] = someMap.mapValues {
        case x:Boolean => x
        case _ => true //or whatever should be taken when answer is string
      }.withDefaultValue(false)
      //Oh... and keep in mind that mapValues is lazy... without good reason. //You should not use any side effects using it !!! 
      
      
      someMap("key4")
      
      

      【讨论】:

      • 很遗憾,我的应用程序是用 2.11 编写的。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-03-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-11-27
      相关资源
      最近更新 更多