6 votes

Comment puis-je valider les valeurs des options avec la validation Cats ?

J'essaie de modifier un code qui utilise la validation des chats, quelque chose comme.. :

  case class Example(text: String, image: String)
  case class ValidExample(text: String, image: String)

  import cats.data.Validated._
  import cats.implicits._

  def validText(text: String) = if (text.nonEmpty) text.valid else invalid(-1)
  def validImage(image: String) = if (image.endsWith(".png")) image.valid else invalid(-1)
  val e = Example("test", "test.png")
  (validText(e.text), validImage(e.image)).mapN(ValidExample)

Ce qui fonctionne bien.

Mais mon changement nécessite que le champ image soit une option, comme :

  case class Example(text: String, image: Option[String])
  case class ValidExample(text: String, image: Option[String])

  import cats.data.Validated._
  import cats.implicits._

  def validText(text: String) = if (text.nonEmpty) text.valid else invalid(-1)
  def validImage(image: String) = if (image.endsWith(".png")) image.valid else invalid(-1)
  val e = Example("test", Some("test.png"))
  (validText(e.text), e.image.map(validImage)).mapN(ValidExample)

Avec cela, le mapN échoue parce que les types diffèrent soudainement, dit-il :

value mapN is not a member of (cats.data.Validated[Int,String], Option[cats.data.Validated[Int,String]])

Je veux qu'il ne valide que si la valeur existe. Il doit donc faire partie du résultat de la validation si la valeur est présente, mais ignorer le champ dans le cas contraire. Je sais qu'il existe des moyens de combiner les validations, mais dans mon code réel, cela serait beaucoup plus compliqué que quelque chose comme ceci.

Existe-t-il un moyen de le faire de manière simple ? Je n'ai rien trouvé à ce sujet dans la documentation ou dans la recherche.

Merci pour votre aide !

8voto

Travis Brown Points 56342

La réponse est traverse , comme d'habitude :) :

scala> val exampleWithImage = Example("test", Some("test.png"))
exampleWithImage: Example = Example(test,Some(test.png))

scala> val exampleWithoutImage = Example("test", None)
exampleWithoutImage: Example = Example(test,None)

scala> val badExampleWithImage = Example("test", Some("foo"))
badExampleWithImage: Example = Example(test,Some(foo))

scala> exampleWithImage.image.traverse(validImage)
res1: cats.data.Validated[Int,Option[String]] = Valid(Some(test.png))

scala> exampleWithoutImage.image.traverse(validImage)
res2: cats.data.Validated[Int,Option[String]] = Valid(None)

scala> badExampleWithImage.image.traverse(validImage)
res3: cats.data.Validated[Int,Option[String]] = Invalid(-1)

Donc, il semble que traverse sur Option fait ce que vous voulez : il valide le contenu du fichier Some et ignore None (c'est-à-dire qu'il le laisse passer comme étant valide).

Vous pourriez donc écrire ce qui suit, en remplaçant map avec traverse :

scala> (validText(e.text), e.image.traverse(validImage)).mapN(ValidExample)
res4: cats.data.Validated[Int,ValidExample] = Valid(ValidExample(test,Some(test.png)))

Et vous avez terminé.

3voto

Ivan Stanislavciuc Points 1680

Appelez sequence sur Option[Validated[String]] à l'endroit e.image.map(validImage)

import cats.data._
import cats.data.Validated._
import cats.implicits._

case class Example(text: String, image: Option[String])
case class ValidExample(text: String, image: Option[String])

def validText(text: String) = if (text.nonEmpty) text.valid else invalid(-1)
def validImage(image: String) = if (image.endsWith(".png")) image.valid else invalid(-1)
val e = Example("test", Some("test.png"))
println((validText(e.text), e.image.map(validImage).sequence).mapN(ValidExample))

produit

Valid(ValidExample(test,Some(test.png)))

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X