【问题标题】:Working with options in Scala (best practices)在 Scala 中使用选项(最佳实践)
【发布时间】:2018-06-02 22:27:02
【问题描述】:

我编写了一个方法,通过执行 API 调用并添加丰富的数据来丰富个人数据。

我有这个案例类:

case class Person(personData: PersonData, dataEnrichment: Option[DataEnrichment])

我的方法应该返回这个案例类,但我之前有几个过滤器,如果人的高度不是"1.8 m" 或者如果使用正则表达式在生物中找不到 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 的最佳实践。您有更优雅的方法来获得相同的结果吗?

【问题讨论】:

    标签: scala scala-option


    【解决方案1】:

    使用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 调用链,但更容易阅读。

    【讨论】:

      【解决方案2】:

      在这里,我试图让它更惯用和更短:

      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 的适当一元链而不是模式匹配。

      【讨论】:

        【解决方案3】:

        与 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))
            }
        

        这对我来说更具可读性。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2017-12-19
          • 2014-11-18
          • 2011-05-03
          • 2020-04-04
          • 1970-01-01
          • 2013-09-10
          • 2018-07-01
          • 1970-01-01
          相关资源
          最近更新 更多