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
Related
I want to compute something if exactly one of two options is non-empty. Obviously this could be done by a pattern match, but is there some better way?
(o1, o2) match {
case (Some(o), None) => Some(compute(o))
case (None, Some(o)) => Some(compute(o))
case _ => None
}
You could do something like this:
if (o1.isEmpty ^ o2.isEmpty)
List(o1,o2).flatMap(_.map(x=>Some(compute(x)))).head
else
None
But pattern matching is probably the better way to go.
Thanks to helpful comments from #Suma, I came up with another solutions in addition to the current ones:
Since the inputs are always in the form of Option(x):
Iterator(Seq(o1,o2).filter(_!=None))
.takeWhile(_.length==1)
.map( x => compute(x.head.get))
.toSeq.headOption
Using iterator also allows for a sequence of values to be passed to the input. The final mapping will be done if and only if one value in the sequence is defined.
Inspired by now deleted answer of pedrofurla, which was attempting to use o1 orElse o2 map { compute }, one possibility is to define xorElse, the rest is easy with it:
implicit class XorElse[T](o1: Option[T]) {
def xorElse[A >: T](o2: Option[A]): Option[A] = {
if (o1.isDefined != o2.isDefined) o1 orElse o2
else None
}
}
(o1 xorElse o2).map(compute)
Another possibility I have found is using a pattern match, but using Seq concatenation so that both cases are handled with the same code. The advantage of this approach is it can be extended to any number of options, it will always evaluate when there is exactly one:
o1.toSeq ++ o2 match {
case Seq(one) => Some(compute(one))
case _ => None
}
Just initialize a sequence and then flatten
Seq(o1, o2).flatten match {
case Seq(o) => Some(compute(o))
case _ => None
}
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)
}
I have the following input string:
"0.3215,Some(0.5123)"
I would like to retrieve the tuple (0.3215,Some(0.5123)) with: (BigDecimal,Option[BigDecimal]).
Here is one of the thing I tried so far:
"\\d+\\.\\d+,Some\\(\\d+\\.\\d+".r findFirstIn iData match {
case None => Map[BigDecimal, Option[BigDecimal]]()
case Some(s) => {
val oO = s.split(",Some\\(")
BigDecimal.valueOf(oO(0).toDouble) -> Option[BigDecimal](BigDecimal.valueOf(lSTmp2(1).toDouble))
}
}
Using a Map and transforming it into a tuple.
When I try directly the tuple I get an Equals or an Object.
Must miss something here...
Your code has several issues, but the big one seems to be that the case None side of the match returns a Map but the Some(s) side returns a Tuple2. Map and Tuple2 unify to their lowest-common-supertype, Equals, which is what you're seeing.
I think this is what you're trying to achieve?
val Pattern = "(\\d+\\.\\d+),Some\\((\\d+\\.\\d+)\\)".r
val s = "0.3215,Some(0.5123)"
s match {
case Pattern(a,b) => Map(BigDecimal(a) -> Some(BigDecimal(b)))
case _ => Map[BigDecimal, Option[BigDecimal]]()
}
// Map[BigDecimal,Option[BigDecimal]] = Map(0.3215 -> Some(0.5123))
If I have a following method
def getMyList :\/[Throwable,List[\/[Throwable,Int]]] ={
....
}
how to flatten type of getMyList to \/[Throwable,List[Int]]
Just flatMap and sequenceU, it's all in scalaz:
def flatten(e: \/[Throwable,List[\/[Throwable,Int]]]): \/[Throwable,List[Int]] = {
e.flatMap(a => a.sequenceU)
}
If by flatten, you mean remove the left types from List[\/[Throwable,Int]], then you can map the outer disjunction, and collect the right types:
list.map(_.collect{ case \/-(x) => x})
I don't think that some higher order "flatten" exists for /. Looks like Validateion & ValidationNEL will be better choice for this problem. However here is "dirty" solution for /, it will return first fail. If you want to accumulate failures Validation is way to go
val getMyList: \/[Throwable,List[\/[Throwable,Int]]] =
//\/-(List(-\/(new RuntimeException("test")), \/-(1)))
\/-(List(\/-(2), \/-(1)))
val flatten = getMyList.fold(\/.left, _.foldLeft(\/.right[Throwable, List[Int]](List.empty[Int])) {
case (\/-(list), \/-(i)) => \/-(list :+ i)
case (\/-(list), -\/(err)) => -\/(err)
case (-\/(err), _) => -\/(err)
})
println(flatten)
We use the following method, where .sSuccess creates a \/[_, Seq[T]] and .sFail creates a \/[Throwable, _] with all of the throwables' error messages concatenated:
implicit class CondenseEither[T](seq: Seq[\/[Throwable,T]]) = {
def condenseSeq: \/[Throwable, Seq[T]] = {
val errs = seq.filter(_.isLeft).map(_.toEither)
if(errs.isEmpty) seq.map(_.toEither).map(_.right.get).sSuccess
else errs.map(_.left.get.getMessage).mkString(", ")).sFail
}
}
There's probably a way to do this without the toEithers
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))