在Scala中使用flatMap时如何将两个Option [String]变量合并为一个?

时间:2016-05-19 08:28:11

标签: scala functional-programming

我有以下课程:

case class Profile(email: Option[String],
                   firstName: Option[String],
                   lastName: Option[String],
                   fullName: Option[String])

现在我要删除fullName属性,因为它是多余的。但是,我的班级User中有一个方法会返回fullName

case class User(id: UUID, profiles: List[Profile]) {
// Skipped some lines
  def fullName(loginInfo:LoginInfo) = profileFor(loginInfo).flatMap(_.fullName)
}

现在,我尝试将.flatMap(_.fullName)部分替换为firstName + lastName的连接。如何才能做到这一点?我需要制作一个新的Option[String],如下所示:

def fullName(loginInfo:LoginInfo) = {
  val firstName = profileFor(loginInfo).flatMap(_.firstName)
  val lastName = profileFor(loginInfo).flatMap(_.lastName)
  val fullName : Option[String] = Some(firstName + " " + lastName)
  fullName
}

6 个答案:

答案 0 :(得分:4)

这是一种方法

List(firstName, lastName).flatten match {
  case Nil => None
  case xs => Some(xs.mkString(" "))
}

快速测试REPL ......

scala> def fullName(fn: Option[String], ln: Option[String]): Option[String] = {
     |   List(fn, ln).flatten match {
     |     case Nil => None
     |     case xs => Some(xs.mkString(" "))
     |   }
     | }
fullName: (fn: Option[String], ln: Option[String])Option[String]

scala> fullName(None, None)
res3: Option[String] = None

scala> fullName(Some("a"), None)
res4: Option[String] = Some(a)

scala> fullName(None, Some("b"))
res5: Option[String] = Some(b)

scala> fullName(Some("a"), Some("b"))
res6: Option[String] = Some(a b)

答案 1 :(得分:3)

我认为这是for的良好应用。

case class User(id: UUID, profiles: List[Profile]) {
// Skipped some lines
  def fullName(loginInfo:LoginInfo): Option[String] = for {
    profile <- profileFor(loginInfo)
    first <- profile.firstName
    last <- profile.lastName
  } yield s"$first $last"
}

答案 2 :(得分:2)

map2(参见&#34的第4章; the red book&#34;)为您提供了一些抽象:

def map2[A, B, C](oa: Option[A], ob: Option[B])(f: (A, B) => C): Option[C] =
  for {
    a <- oa
    b <- ob
  } yield f(a, b)

然后,将LoginInfo内容留下(因为您没有在任何地方定义profileFor),您只需将fullName定义为

def fullName: Option[String] = map2(firstName, lastName) { _ + " " + _ }

答案 3 :(得分:1)

我们可以将Option视为一个集合,并以简单的单行方式获取您正在寻找的内容:

val firstName: Option[String] = Some("John")
val lastName: Option[String] = Some("Doe")
val fullName: Option[String] = (firstName ++ lastName).reduceOption(_ + " " + _) // Some("John Doe")

答案 4 :(得分:1)

我实际上不久前用一些原生的Scala选项写了一个blog post来做这件事。

我最喜欢的那个与使用reduceLeftOption的{​​{3}}类似:

scala> val firstName = Some("yuval")
firstName: Some[String] = Some(yuval)

scala> val lastName = Some("itzchakov")
lastName: Some[String] = Some(itzchakov)

scala> (firstName ++ lastName).reduceLeftOption((a,b) => s"$a $b")
res10: Option[String] = Some(yuval itzchakov)

这种方法很好,因为当Option[T]中的任何一个Nonescala> val lastName: Option[String] = None lastName: Option[String] = None scala> (firstName ++ lastName).reduceLeftOption((a,b) => s"$a $b") res11: Option[String] = Some(yuval) 时它都有效:

scala> :paste
// Entering paste mode (ctrl-D to finish)

def reduce[T](options: Option[T]*)(f: (T, T) => T) = {
  options.flatten.reduceLeftOption(f)
}
reduce(Some(1), Some(1), Some(2), Some(4))(_+_)

// Exiting paste mode, now interpreting.

reduce: [T](options: Option[T]*)(f: (T, T) => T)Option[T]
res0: Option[Int] = Some(8)

另一个不错的属性是它在使用varargs时也适用于 N 元素:

getProfile()

答案 5 :(得分:0)

我认为从oop的角度来看,您应该fullNameProfile课程中,但作为一种方法,您的User课程只需将其委托给Profile

你可以使用已经给出的任何解决方案,我会发布我的,使用scalaz和applicative builder,它类似于map2,只是更一般,因为你可以加入任何(好吧,不是真的,但是合理的金额,因为您必须传递与您想要应用的对象数量相同的参数数量的函数,以及选项或其他应用程序的数量。

@ case class Profile(firstName: Option[String], lastName: Option[String]) {
  def fullName: Option[String] = (firstName |@| lastName)(_ + " " + _)
}

@ Profile("A".some, "B".some).fullName 
res2: Option[String] = Some("A B")
@ Profile("A".some, none).fullName 
res3: Option[String] = None
@ Profile(none, "B".some).fullName 
res4: Option[String] = None
@ Profile(none, none).fullName 
res5: Option[String] = None