繁体   English   中英

Scala-Cats:验证Map的条目

[英]Scala-Cats: Validating Map's entries

假设我有:

val m: Map[String, Int] = Map("one" -> 1, "five" -> 5, "six" -> 6, "nine" -> 9)

我有两个功能:

def isNotDivisibleByTwo(i: Int): ValidatedNec[String, Int] = Validated.condNec(i%2!=0, i, s"$i is divisible by 2.")

def isNotDivisibleByThree(i: Int): ValidatedNec[String, Int] = Validated.condNec(i%3!=0, i, s"$i is divisible by 3.")

我想要一个给我的功能:

def sanitize(m: Map[String, Int]):Map[String, Validated[NonEmptyList[String], Int]] = ???

它应该返回满足上述两个功能的所有数字,以及所有失败数字及其相关故障的映射。
例如对于给定的列表m,我想得到:

val result = Map(
  "one" -> Valid(1),
  "five -> Valid(5),
  "nine" -> Invalid(NonEmptyList("9 is dividible by 3")),
  "six" -> Invalid(NonEmptyList("6 is dividible by 2", "6 is dividible by 3"))
)

这是我目前拥有的:

import cats.data._

val m: Map[String, Int] = Map("one" -> 1, "five" -> 5, "six" -> 6, "nine" -> 9)

def isNotDivisibleByTwo(i: Int): ValidatedNec[String, Unit] = Validated.condNec(i%2!=0, (), s"$i is divisible by 2.")

def isNotDivisibleByThree(i: Int): ValidatedNec[String, Unit] = Validated.condNec(i%3!=0, (), s"$i is divisible by 3.")


def sanitize(m: Map[String, Int]): Map[String, Validated[NonEmptyChain[String], Int]] = {

  m.mapValues{
    i =>
      isNotDivisibleByTwo(i).product(
        isNotDivisibleByThree(i)
      ).map(_ => i)
  }

}

但是,我对“编写”验证的方式不满意。

我该如何以最轻松的方式进行此操作?

你好亲近
请记住,组合多个Validate的正确方法是使用Applicative语法。

import cats.data.{Validated, ValidatedNec}
import cats.syntax.apply._

type ErrorsOr[A] = ValidatedNec[String, A]

def isNotDivisibleByTwo(i: Int): ErrorsOr[Int] =
  Validated.condNec((i % 2) != 0, i, s"$i is divisible by 2.")

def isNotDivisibleByThree(i: Int): ErrorsOr[Int] =
  Validated.condNec((i % 3) != 0, i, s"$i is divisible by 3.")

val map: Map[String, Int] = Map("one" -> 1, "five" -> 5, "six" -> 6, "nine" -> 9)

def sanitize(m: Map[String, Int]): Map[String, ErrorsOr[Int]] =
  m.view.mapValues { i =>
    (
      isNotDivisibleByTwo(i),
      isNotDivisibleByThree(i)
    ).tupled.map(_ => i)
  }.toMap

sanitize(map)
// res: Map[String, ErrorsOr[Int]] = Map(
//   "one" -> Valid(1),
//   "five" -> Valid(5),
//   "six" -> Invalid(Append(Singleton("6 is divisible by 2."), Singleton("6 is divisible by 3."))),
//   "nine" -> Invalid(Singleton("9 is divisible by 3."))
// )

但是,您可以使代码更加通用,以使用任意数量的验证。 通过使用traverse
(在这种情况下,您不需要任何语法导入)

import cats.data.NonEmptyList

val validations: NonEmptyList[Int => ErrorsOr[Int]] = NonEmptyList.of(isNotDivisibleByTwo, isNotDivisibleByThree)

def sanitize[K, V](map: Map[K, V])
                  (validations: NonEmptyList[V => ErrorsOr[V]]): Map[K, ErrorsOr[V]] =
  map.view.mapValues(i => validations.traverse(f => f(i)).map(_ => i)).toMap

sanitize(map)(validations)
// res: Map[String, ErrorsOr[Int]] = Map(
//   "one" -> Valid(1),
//   "five" -> Valid(5),
//   "six" -> Invalid(Append(Singleton("6 is divisible by 2."), Singleton("6 is divisible by 3."))),
//   "nine" -> Invalid(Singleton("9 is divisible by 3."))
// )

我之所以使用.view.mapValues(...).toMap的原因是因为在Scala 2.13 mapValues已弃用了mapValues

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM