【问题标题】:How to gracefully handle "Key not found" In a Scala map?如何优雅地处理 Scala 地图中的“未找到密钥”?
【发布时间】:2015-11-28 09:54:07
【问题描述】:

总而言之,如果在 Map 中找到值,我需要一种功能方法来做一件事,如果没有找到,我需要做另一件事请注意,我对返回的值不感兴趣,但对已完成的操作不感兴趣。

继续阅读了解详情。

我有一个服务名称映射(友好的)到 URL PATH 的一部分(不容易记住)。 这里是初始化。

val serviceMap = Map("read" -> "cryptic-read-path",
  "save" -> "cryptic-save-path", "county" -> "cryptic-zip-code-to-county-service.");

我所知道的所有替代方案都会导致 if 语句加载了 then 部分,但很简单,404 else 部分。 以下是我想到的一些内容

if (serviceMap.contains(service)) {
    //Do stuff
} else {
    //Issue a 404
}

相当于谓词颠倒

if (!serviceMap.contains(key)) //issue a 404
//Do stuff

上述两种方法都需要我先检查然后获取。

另一个(被Option documentation劝阻)

  serviceMap.get(service) match {
  case _ : Option[String]=> //Do stuff
  case _  => //issue 404
}

还有第三个

  serviceMap.get(service).forEach {//Dostuff and return, since it's just one element }
  //issue 404 if you are here.

根据Option documentation,我鼓励我将Option 视为一个集合,所以最后一种选择似乎更好,但使用forEach 在循环后更改执行流程似乎很奇怪。

这是我的经验不足,但所有这些对我来说都显得过于冗长和不合适。你们能给我一个更好的选择或评论这些方法吗? 感谢期待!

【问题讨论】:

  • 我是堆栈交换的新手。看到完全陌生的人的支持真的很感动!
  • 您的问题很好,因为它显示了研究工作和您尝试过的内容。好问题得到好答案。

标签: scala dictionary scala-collections


【解决方案1】:

我认为惯用的方法是使用mapgetOrElse

val result = serviceMap.get(service).map { url =>
  // do something with url
} getOrElse {
  // not found
}

一般来说,不要将Option 视为循环(这不是函数式方法)的单元素集合,而是转换成别的东西。

【讨论】:

  • 感谢您的帮助!
【解决方案2】:

几个更清晰的选项:

  1. matchunapply(避免了 _ : Option[CantTellWhatThisIsAtRuntime] 的类型擦除问题):

    serviceMap.get(service) match {
      case Some(path) => // Do stuff and return
      case _ => // 404
    }
    
  2. 使用fold 提取值(假设您可以正确键入 404):

    serviceMap.get(service).fold(/* 404 */) { path =>
      // Do stuff and return
    }
    
  3. 使用map + getOrElse 将路径转换为响应或返回/抛出 404 (hat tip to Mikesname):

    serviceMap.get(service).map { path =>
      // Do stuff and return
    } getOrElse {
      // 404
    }
    
  4. 使用MapgetOrElse 方法(假设你抛出你的404):

    val path = serviceMap.getOrElse(service, throw Error404)
    // Do stuff and return
    

【讨论】:

  • 谢谢,但是折叠 (/* 404 */) 中的z 部分不会一直执行吗?
  • 不——除非OptionNone(它是别名=> T 而不是引用T),否则它不会被执行。你可以在 REPL 中自己尝试一下:(None: Option[String]).fold({println("Runs"); "Ha"})(_ + "Hi") vs. Option("Hi").fold({println("Does not run"); "Ha"})(_ + "Hi")
  • 感谢您帮助我理解!
  • 对我来说,最后一个选项有效(#4)最好的原因是我需要在 404 上抛出异常。
【解决方案3】:

就我个人而言,我会使用模式匹配来做到这一点。它使它非常具有可读性。

serviceMap.get(service) match {
  case Some(s) => println("Here's my string from the map! " + s)
  case _ => //issue 404
}

此外,代码可以很容易地修改。例如,如果在将来的某个时间点你需要专门针对映射到“cryptic-read-path”的值做一些事情,你可以这样做:

serviceMap.get(service) match {
  case Some("cryptic-read-path") => //do stuff that is specific for cryptic read path
  case Some(s) => println("Here's my string from the map! " + s)
  case _ => //issue 404
}

对于第三个选项,您建议在 foreach 中返回。这将导致非本地返回,这是无效的。 阅读更多关于它的信息here

编辑: 如果您不需要 get 调用的结果,也可以这样做:

serviceMap.get(service) match {
  case Some(_) => //do stuff
  case _ => //issue 404
}

【讨论】:

  • 是的,for/each 非本地返回从一开始就让我很烦恼,但感谢您提供更多信息。
【解决方案4】:

您的目标是根据是否存在的价值执行不同的操作。虽然模式匹配是将Option 转换为值的一种不太惯用的方式,但我会说它完全适合作为在多个操作之间进行选择的分支机制。出于这个原因,我建议使用您提出的 match 替代方案。

但是,如果您不同意它是适当的,请考虑将您的问题视为将Option 转换为操作之一:

 val response = serviceMap.get(service).map { value =>
   // do stuff to convert into a 200 response
 }.getOrElse {
   // create 404 response
 }

map 将任何Some[String] 转换为Some[Response],同时保持None。现在您有了Option[Response],您可以使用getOrElse 将您的404 响应替换为None 的可能性。

【讨论】:

  • 感谢您的帮助。
【解决方案5】:

Map 提升到仅为映射键定义的部分函数,​​例如,对于给定的映射

val a = Map( 1->2, 3->4)

我们可以解除a

a.lift
Int => Option[Int] = <function1>

等等

a.lift(1)
Option[Int] = Some(2)

a.lift(0)
Option[Int] = None

然后考虑像这样的 for 理解,

val ok = for { v <- a.lift(1); res <- Some(awesome_stuff(v)) }
         yield res

ok getOrElse report404

如果vres不成功(即None),那么ok就是None

【讨论】:

    猜你喜欢
    • 2015-09-25
    • 1970-01-01
    • 2011-08-08
    • 1970-01-01
    • 1970-01-01
    • 2011-11-26
    • 1970-01-01
    • 1970-01-01
    • 2023-03-21
    相关资源
    最近更新 更多