如何在Scala地图中优雅地处理“Key not found”?

时间:2015-09-02 18:40:42

标签: scala dictionary scala-collections

总之,如果在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似乎很奇怪,在循环后改变执行流程。

这是我的经验不足,但所有这些看起来都太冗长,对我不合适。你们能给我一个更好的选择或评论方法吗? 在期待中感谢!

5 个答案:

答案 0 :(得分:4)

我认为习惯的方式是使用mapgetOrElse

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

一般情况下,不要将Option视为循环的单元素集合(这不是功能方法),而是转换进入别的东西。

答案 1 :(得分:3)

更清晰的几个选项:

  1. match unapply(避免了_ : 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. 使用Map' getOrElse方法(假设你扔掉了404):

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

答案 2 :(得分:3)

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

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

此外,可以轻松修改代码。例如,如果在将来某个时候你需要做一些事情,如果专门针对映射到“神秘读取路径”的值,你可以这样做:

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
}

答案 3 :(得分:1)

您的目标是根据现有或不存在的值执行不同的操作。虽然模式匹配是一种将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的可能性。

答案 4 :(得分:1)

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

然后考虑一下如此理解,例如

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

ok getOrElse report404

如果vres未成功(即None),则okNone