将带有Options的案例类转换为不带选项的案例类

时间:2018-03-13 09:52:17

标签: scala

使用嵌套选项的case类如何通过提取None并将case class UserPreferencesOpt(animals: Option[AnimalPreferencesOpt]) case class AnimalPreferencesOpt(dogs: Option[String], cats: Option[String]) case class UserPreferences(animals: AnimalPreferences) object UserPreferences { def getDefault: UserPreferences(AnimalPreferences("bark", "meow") } case class AnimalPreferences(dogs: String, cats: String) 替换为默认值来将其转换为没有选项的双案例类。

假设我们有:

UserPreferencesOpt(Some(AnimalPreferencesOpt(Some("Dogs are cool!"), None)))

所以基本上,我想转换:

UserPreferences(AnimalPreferences("Dogs are cool!", "meow")

成:

WeatherPreferences

通过在存在时提取选项并替换缺少默认值。

可以通过模式匹配和按值检查来完成,但这只是一个例子。显然,在很多情况下,域逻辑中会有更多的嵌套首选项,我想知道是否可以转换它而不添加额外的匹配和添加新首选项的情况(例如。const request = async () => { const response = await fetchIp(); $rootScope.nodeIp = (response.length == 0 ? "localhost" : response[0]); }; request(); var root = 'http://' + $rootScope.nodeIp + ':8080/'; 甚至是UserPreferences中的某些内容)

听说过像Shapeless或宏这些高级Scala的东西我认为有人可能会推荐它,但有没有办法用惯用,功能,Scala-ish 方式实现它?

4 个答案:

答案 0 :(得分:1)

您可以在目标案例类的随播对象的apply中指定默认值。 E.g:

case class UserPreferencesOpt(animals: Option[AnimalPreferencesOpt] = None)
case class AnimalPreferencesOpt(dogs: Option[String] = None, cats: Option[String] = None)

case class UserPreferences(animals: AnimalPreferences)
object UserPreferences {
  def apply(o: UserPreferencesOpt): UserPreferences = {
    UserPreferences(
      AnimalPreferences(
        o.animals.getOrElse(AnimalPreferencesOpt())
      ))
  }
}
case class AnimalPreferences(dogs: String, cats: String)

object AnimalPreferences {
  def apply(opt: AnimalPreferencesOpt): AnimalPreferences = {
    AnimalPreferences(
      opt.dogs.getOrElse("bark"),
      opt.cats.getOrElse("meow")
    )
  }
}

但我同意@ pedrorijo91这个模型看起来很奇怪..

答案 1 :(得分:1)

如果默认值是静态的,那么更简单的解决方案是在实例构造中填充它,而不是具有选项的类。

对于您的示例,那将是:

case class UserPreferences(animals: AnimalPreferences)
case class AnimalPreferences(dogs: String, cats: String)

object UserPreferences {
  def apply(dogOpt: Option[String], catOpt: Option[String]): UserPreferences = {
    val dog = dogOpt.getOrElse("bark")
    val cat = catOpt.getOrElse("meow")
    UserPreferences(AnimalPreferences(dog, cat))
  }
}

// then you can create, eg:
UserPreferences(None, None)
UserPreferences("Grrr", None)
UserPreferences("Dogs are cool!", "meow")

答案 2 :(得分:1)

解决此问题的另一种 scala-ish 方法是使用implicit conversions。您必须定义将{转换为类转换为所需类的implicit methods,例如他们各自的同伴班级中UserPreferenceOptUserPreference

  case class UserPreferencesOpt(animals: Option[AnimalPreferencesOpt])
  case class AnimalPreferencesOpt(dogs: Option[String], cats: Option[String])

  case class UserPreferences(animals: AnimalPreferences)
  case class AnimalPreferences(dogs: String, cats: String)

  //Note that, I have used `null` value in case of `None` found in Opt class. Instead of `null`, you can provide default value with some logic here.
  object UserPreferencesOpt {
    implicit def optToUserPref(userPref: UserPreferencesOpt): UserPreferences = UserPreferences(userPref.animals.getOrElse(null))
  }
  object AnimalPreferencesOpt {
    implicit def optToAnimalPref(animalPref: AnimalPreferencesOpt): AnimalPreferences = AnimalPreferences(animalPref.dogs.getOrElse(null), animalPref.cats.getOrElse(null))
  }
  val userPrefOpt:UserPreferencesOpt = UserPreferencesOpt(Some(AnimalPreferencesOpt(Some("Dogs are cool!"), None)))
  val userPref: UserPreferences = userPrefOpt

答案 3 :(得分:1)

library支持此功能,如下所示:

import cats.data.Validated
import cats.implicits._
import henkan.optional.all._

case class Message(a: Option[String], b: Option[Int])
case class Domain(a: String, b: Int)

validate(Message(Some("a"), Some(2))).to[Domain]
// res0: henkan.optional.ValidateFromOptional.Result[Domain] = Valid(Domain(a,2))

validate(Message(Some("a"), None)).to[Domain]
// res1: henkan.optional.ValidateFromOptional.Result[Domain] = Invalid(NonEmptyList(RequiredFieldMissing(b)))