首页 > 解决方案 > 使用 scala 猫检查非空字段的组合

问题描述

在我的代码中,我有一个类来检查它是否有效我应该评估是否存在至少一个可能的字段组合(通过存在是指组合的每个字段都不应该为空)。例子:

case class Test( a: Option[String]
               , b: Option[String]
               , c: Option[String]
               , d: Option[String]
               , e: Option[Double]
               , f: Option[Double])

要“有效”,必须至少存在以下字段组合之一(“a,b,c”,“a,d,e”,“a,f”)

我试图用 scala 猫库来做到这一点,但我有点迷路了。任何建议将不胜感激。

标签: scalascala-cats

解决方案


如果你想验证它,你可以:

case class Test( a: Option[String]
               , b: Option[String]
               , c: Option[String]
               , d: Option[String]
               , e: Option[Double]
               , f: Option[Double]) {
  // "a,b,c","a,d,e","a,f"
  def isValid = (a.isDefined && b.isDefined && c.isDefined) ||
                (a.isDefined && d.isDefined && e.isDefined) ||
                (a.isDefined && f.isDefined)
}

如果要确保仅在定义了一个字段时才能创建它,则必须使用智能构造函数

sealed abstract case class Test private ( a: Option[String]
                                        , b: Option[String]
                                        , c: Option[String]
                                        , d: Option[String]
                                        , e: Option[Double]
                                        , f: Option[Double])
object Test {

  def create( a: Option[String]
            , b: Option[String]
            , c: Option[String]
            , d: Option[String]
            , e: Option[Double]
            , f: Option[Double]): Either[String, Test] =
  if ((a.isDefined && b.isDefined && c.isDefined) ||
      (a.isDefined && d.isDefined && e.isDefined) ||
      (a.isDefined && f.isDefined))
    Right(new Test(a, b, c, d, e, f) {})
  else
    Left("All arguments are empty")
}

或者使用 ADT 确保定义了一个字段:

sealed trait Test extends Product with Serializable
object Test {
  final case class Case1( a: String
                        , b: String
                        , c: String
                        , d: Option[String]
                        , e: Option[Double]
                        , f: Option[Double]) extends Test
  final case class Case2( a: String
                        , b: Option[String]
                        , c: Option[String]
                        , d: String
                        , e: String
                        , f: Option[Double]) extends Test
  final case class Case3( a: String
                        , b: Option[String]
                        , c: Option[String]
                        , d: Option[String]
                        , e: Option[Double]
                        , f: Double) extends Test
}

你可以在这里使用 Cats ......但是为了什么?您没有将元组或选项集合组合成一个选项。你不换成F[Option[X]Option[F[X]相反。没有副作用,映射,遍历,从嵌入在某些上下文中的较小对象构造新对象等。您可以尝试执行以下操作

implicit val booleanMonoid: Monoid[Boolean] = new Monoid[Boolean] {
  def combine(a: Boolean, b: Boolean) = a && b
  def empty = true
}
def isValid = List(a, b, c).foldMap(_.isDefined) ||
              List(a, d, e).foldMap(_.isDefined) ||
              List(a, f).foldMap(_.isDefined)

甚至可能

def isValid = (
  (a, b, c).tupled.void orElse (a, d, e).tupled.void orElse (a, f).tupled.void
).isDefined

但这几乎没有比

def isValid = List(a, b, c).exists(_.isDefined) ||
              List(a, d, e).exists(_.isDefined) ||
              List(a, f).exists(_.isDefined)

可以在香草 Scala 中实现。我想你可以通过使用一些Ring定义的符号来简单地Option[_]使用*+

implicit val ring: Ring[Option[_]] = ... // yup, existential type here
def isValid = ((a * b * c) + (a * d * e) + (a * f)).isDefined

(这将需要typelevel algebra)但是仅在一个地方使用它我看不到收益。


推荐阅读