在Scala中处理选项的最佳实践

6
我有一个方法,通过调用API并添加丰富的数据来丰富个人数据。
我有以下案例类:
case class Person(personData: PersonData, dataEnrichment: Option[DataEnrichment])

我的方法应该返回这个case类,但是在此之前我有几个过滤器,以防人的身高不是"1.8米",或者如果没有在生物中使用正则表达式找到personId,我想要返回Person并且dataEnrichment=None。我的问题是person height和personId本身就是Options,所以它看起来像这样:

   def enrichPersonObjWithApiCall(person: Person) = {
    
      person.personData.height.map(_.equals("1.8 m")) match {
        case Some(true) =>
          val personId = person.personData.bio flatMap { comment =>
            extractPersonIdIfExists(comment)
          }
          personId match {
            case Some(perId) =>
              apiCall(perId) map { apiRes =>
                Person(
                  person.personData,
                  dataEnrichment = apiRes)
              }
            case _ =>
              Future successful Person(
                person.personData,
                dataEnrichment = None)
          }
        case _ =>
          Future successful Person(
            person.personData,
            dataEnrichment = None)
      }
    }
    
    def extractPersonIdIfExists(personBio: String): Option[String] = {
      val personIdRegex: Regex = """(?<=PersonId:)[^;]+""".r
      personIdRegex.findFirstIn(personBio)
    }
    
    def apiCall(personId: String): Future[Option[DataEnrichment]] = {
      ???
    }
    
    case class DataEnrichment(res: Option[String])
    
    case class PersonData(name: String, height: Option[String], bio: Option[String])
    

似乎这不是Scala的最佳实践。你有更优雅的方法来达到相同的结果吗?

3个回答

2

使用 for 循环是处理一系列 Option 值的好方法:

def enrichPersonObjWithApiCall(person: Person): Future[Person] =       
  (
    for {
       height <- person.personData.height if height == "1.8 m"
       comment <- person.personData.bio
       perId <- extractPersonIdIfExists(comment)
     } yield {
       apiCall(perId).map(Person(person.personData, _))
     }
  ).getOrElse(Future.successful(Person(person.personData, None)))

这相当于一系列mapflatMapfilter调用的链式结构,但更易于阅读。

0

在这里,我尝试使其更符合习惯用语且更短:

def enrichPersonObjWithApiCall(person: Person) = {
  person.personData.height.collect {
    case h if h == "1.8 m" =>
      val personId = person.personData.bio.flatMap(extractPersonIdIfExists)

      personId.map(
        apiCall(_)
          .map(apiRes => person.copy(dataEnrichment = apiRes))
      )
  }.flatten.getOrElse(
    Future.successful(person.copy(dataEnrichment = None))
  )
}

基本上,这个想法是在适当的情况下使用适当的单子链 mapflatMapcollect,而不是使用模式匹配。


0

和Aivean的答案一样的想法。只是我会使用mapflatMapfilter

   def enrichPersonObjWithApiCall(person: Person) = {
     person.personData.height
       .filter(_ == "1.8 m")
       .flatMap{_=>
         val personId = person.personData.bio 
                          .flatMap(extractPersonIdIfExists)
         personId.map(
           apiCall(_)
           .map(apiRes => person.copy(dataEnrichment = apiRes))
         )
      }.getOrElse(Future.successful(person))
    }

这对我来说更易读。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接