separate scala list based on matching pattern - scala

i have a list of the following scala trait. How can i separate the list into two, one containing only ValidatedSbcCommand objects and other only containing FailedValidationSbcCommand objects?
sealed trait SbcCommandorOrValidationError
case class ValidatedSbcCommand(sbcCommand: SbcCommand) extends SbcC ommandorOrValidationError
case class FailedValidationSbcCommand(sbcCommandError: SbcCommandError) extends SbcCommandorOr

Use the partition method on list. It takes a predicate and produces a (List, List) The first list is for the true case the second is for false.

val result = originalList.foldRight(Tuple2(List[ValidatedSbcCommand](), List[FailedValidationSbcCommand]())){ (start, rest) =>
start match {
case a:ValidatedSbcCommand => (a::rest._1, rest._2)
case b:FailedValidationSbcCommand => (rest._1, b::rest._2)
case _ => rest
}
}
Then result._1 will give you a list of ValidatedSbcCommand, and result._2 will give you a list of FailedValidationSbcCommand.

I prefer using partition with pattern matching. Given list is of type List[SbcCommandorOrValidationError] and contains only ValidatedSbcCommands and FailedValidationSbcCommands, you can do this:
val (validatedCommands, failedCommands) = list.partition {
case command: ValidatedSbcCommand => true
case _ => false
}
This will return a tuple of type (List[SbcCommandorOrValidationError], List[SbcCommandorOrValidationError]) where the first list is all the ValidatedSbcCommands and the second is all the FailedValidationSbcCommands.
If you need to access the specific subclass later on, don't cast. Use pattern matching as above:
validatedCommands.map {
case c: ValidatedSbcCommand => functionTakingValidatedSbcCommandsOnly(c)
}

From Scala 2.13, you can use of partitionMap, which does exactly what you want, keeping the subtype info:
list partitionMap {
case v: ValidatedSbcCommand => Left(v)
case f: FailedValidationSbcCommand => Right(f)
}

Related

Scala 2.13: avoid repeated case class conversion?

I have a pattern matching for sealed trait which expects two of one case classes as shown below:
expressions.head match {
case SingleValueExpression(value,_operator,_ignoreCase) => filter ++ FromScala(
SingleValueExpression(value,_operator,_ignoreCase)
).transform(ToJson.string)
case MultipleValueExpression(value,_operator,_apply,_instances) => filter ++ FromScala(
MultipleValueExpression(value,_operator,_apply,_instances)
).transform(ToJson.string)
}
You can see that even after decoding the case classes, I am recreating the case class in the next step:
case MultipleValueExpression(value,_operator,_apply,_instances) => MultipleValueExpression(value,_operator,_apply,_instances)
Is there a way to match the pattern such that I could check if the instance is of that case class and then use the value as it is instead of destructuring it and recreating the same case class?
To just check the type in a pattern match, use :, like so:
expressions.head match {
case sve: SingleValueExpression => filter ++ FromScala(sve).transform(ToJson.string)
case mve: MultipleValueExpression => filter ++ FromScala(mve).transform(ToJson.string)
}
You could even have something like
val fromScala =
expressions.head match {
case sve: SingleValueExpression => FromScala(sve)
case mve: MultipleValueExpression => FromScala(mve)
}
filters ++ fromScala.transform(ToJson.string)
You can alternatively use # to do extraction and binding in one case (e.g. you want to match based on a component of a case class but save the overall match):
case sve # SingleValueExpression(value, _, _) if somePredicate(value) =>
// sve is the overall SingleValueExpression, and value is also available in this branch

scala directly map fields of sequence elements

I have a collection with elements that have a field field1. I want to get all field1s that are options of type MyType.
Currently this is my code.
elems.map(_.field1).map {case Some(found: MyType) => found}
I'm sure this can be done in a much nicer way.. It bugs me that I need to use map twice. Is there a way to do this with only one map/collect ?
EDIT: My code works. I'm just wondering if it can be done in a better (i.e. shorter or prettier way).
elems.flatMap(_.field1.collect { case x: MyType => x })
I believe utilising .flatMap may solve this issue for you
elems.flatMap(_.field1 match {
case myType: MyType => Some(myType)
case _ => None
}
Calling iterator before transforming the collection accumulates all the transformations into a single one so perhaps try
elems
.iterator
.flatMap(_.field1)
.collect { case v: MyType => v }
.toList
if your Seq type is case class you can use pattern matching with one collect function like so (see actual seq):
case class MyTypeWrapper(field1: Option[MyType])
case class MyType(x: String)
val elems = Seq.empty[MyTypeWrapper]
val expected: Seq[MyType] = elems.map(_.field1).map{ case Some(found: MyType) => found }
val actual: Seq[MyType] = elems.collect{ case MyTypeWrapper(Some(mt: MyType)) => mt }
// expected and actual should contains the same elements

How to use a Result[String] in Scala match expression

In the following code the first expression returns a Result[String] which contains one of the strings "medical", "dental" or "pharmacy" inside of a Result. I can add .toOption.get to the end of the val statement to get the String, but is there a better way to use the Result? Without the .toOption.get, the code will not compile.
val service = element("h2").containingAnywhere("claim details").fullText()
service match {
case "medical" => extractMedicalClaim
case "dental" => extractDentalClaim
case "pharmacy" => extractPharmacyClaim
}
Hard to say without knowing what Result is. If it's a case class, with the target String as part of its constructor, then you could pattern match directly.
Something like this.
service match {
case Result("medical") => extractMedicalClaim
case Result("dental") => extractDentalClaim
case Result("pharmacy") => extractPharmacyClaim
case _ => // default result
}
If the Result class doesn't have an extractor (the upapply() method) you might be able to add one just for this purpose.
I'm assuming this Result[T] class has a toOption method which returns an Option[T] - if that's the case, you can call toOption and match on that option:
val service = element("h2").containingAnywhere("claim details").fullText().toOption
service match {
case Some("medical") => extractMedicalClaim
case Some("dental") => extractDentalClaim
case Some("pharmacy") => extractPharmacyClaim
case None => // handle the case where the result was empty
}

Scala Map pattern matching

How to do pattern matching on a Map in Scala ?
A (non working) attempt includes,
Map("a"->1, "b"->2, "c"->3) match {
case Map(a,b,_*) => a
}
which errs with
value Map is not a case class, nor does it have an unapply/unapplySeq member
case Map(a,b,_*) => a
The error is indicative enough, yet how to enrich Map with an unapply method for pattern matching ?
Many Thanks
Update
Following #Paul's comment, a neater use case may be like this,
Map("a"->1, "b"->2, "c"->3) match {
case Map("b"->2,_*) => "222"
}
namely, in this case, if map contains key b that maps onto value 2.
Most easy way is tramsform Map to List:
Map("a"->1, "b"->2, "c"->3).to[List] match {
case List(a,b,_*) => a
}
An approach to enriching Map with an unapplySeq method for pattern matching includes this,
object MapExtractor {
def unapplySeq[A <% Ordered[A], B <% Ordered[B]]
(s: Map[A,B]): Option[Seq[(A,B)]] = Some(s.toSeq.sorted)
}
where the sorting approach may be changed to any orderable (items comparable) logic. In this example,
Map("b"->2, "a"->1, "c"->3) match {
case MapExtractor ( x, xs # _* ) => println(s"x: $x") ; println(s"xs: $xs")
}
delivers
x: (a,1)
xs: ArrayBuffer((b,2), (c,3))

Unexpected behaviour with pattern matching on List

Why the following code print one in both cases.
oneOrList(o: Any) = o match {
case List => "list"
case _ => "one"
}
println(oneOrList(1))
println(oneOrList(List(1, 2)))
The List in your case List => is not a type discrimination, it's a test of the target of the match (technically called the scrutinee) against the singleton List object used mainly to provide the List(v1, v2, v3, ...) factory via its apply method.
case List => "list" would match only against List singleton object. So it would match only oneOrList(List)
To match against lists use this:
oneOrList(o: Any) = o match {
case x: List[_] => "list"
case _ => "one"
}
println(oneOrList(1))
println(oneOrList(List(1, 2)))