Catching MatchError at val initialisation with pattern matching in Scala? - scala

What is the best way (concisest, clearest, idiomatic) to catch a MatchError, when assigning values with pattern matching?
Example:
val a :: b :: Nil = List(1,2,3) // throws scala.MatchError
The best way I found so far:
val a :: b :: Nil = try {
val a1 :: b1 :: Nil = List(1,2,3)
List(a1, b1)
catch { case e:MatchError => // handle error here }
Is there an idiomatic way to do this?

Why not simply
val a::b::Nil = List(1,2,3) match {
case a1::b1::Nil => {
a1::b1::Nil
}
case _ => //handle error
}
?

Slightly improving on Kim's solution:
val a :: b :: Nil = List(1, 2, 3) match {
case x # _ :: _ :: Nil => x
case _ => //handle error
}
If you could provide more information on how you might handle the error, we could provide you a better solution.

The following doesn't catch the error but avoids (some of; see Nicolas' comment) it. I don't know whether this is interesting to the asker.
scala> val a :: b :: _ = List(1,2,3)
a: Int = 1
b: Int = 2

The simple solution is this:
List(1, 2, 3) match {
case a :: b :: Nil => .....
case _ => // handle error
}
I don't like to match twice, because it is redundant.
The "val" with pattern matching should only be used when you are sure it matches, or add a try /catch block.

Related

expression type of list[equals] does not conform to expected type seq[either[string,int]]

def convert(list: List[String]): Seq[Either[String, Int]] = list match {
case head :: Nil => Right(head.toInt) :: Nil;
case head :: tail => Right(head.toInt) :: convert(tail) :: Nil;
}
print(convert(List("3", "4", "55")))
I need a seq of either, but it transforms into a list[equlals] and i dont know what to do.
If you want to convert the List[String] into Seq[Either[String,Int]].
val input = List("1", "2", "testString")
//input: List[String] = List(1, 2, testString)
Conversion :
import scala.util.Try
val output: Seq[Either[String,Int]] =
input.map(ele => Try(Right(ele.toInt)).getOrElse(Left(ele))).toSeq
//output: Seq[Either[String,Int]] = List(Right(1), Right(2), Left(testString))
Solution:
def convert(list: List[String]): Seq[Either[String, Int]] = list match {
case head :: Nil => Right(head.toInt) +: Seq.empty;
case head :: tail => Right(head.toInt) +: convert(tail);
}
I like Rumeshs solution much, but if you prefer the pattern matching style:
def convert (list: List [String]): List [Either [String, Int]] = list match {
case Nil => Nil
case head :: tail => if (head.matches ("[0-9]+"))
Right (head.toInt) :: convert (tail) else
Left (head) +: convert (tail)
}
convert (List("3", "four", "55"))
// res304: List[scala.util.Either[String,Int]] = List(Right(3), Left(four), Right(55))
convert (List("3", "4", "55"))
// res305: List[scala.util.Either[String,Int]] = List(Right(3), Right(4), Right(55))
Tranforming it to Seq in the end is easy
convert (List("3", "4", "55")).toSeq
or use your own approach with +: , which will however return a List, since a List is a Seq, but it misses on failing .toInt calls.
I didn't test it - maybe this solution is faster than the Try{} approach, maybe slower, probably depending on how often the matching to an int fails.
Rumesh points out, that list.map works concurrently, so here is the Pattern matching in the String sense for his approach:
List("3", "four", "55") .map (ele =>
if (ele.matches ("[0-9]+")) Right (ele.toInt) else (Left(ele))).toSeq
// res307: scala.collection.immutable.Seq[Product with Serializable with scala.util.Either[String,Int]] = List(Right(3), Left(four), Right(55))
List("3", "4", "55") .map (ele =>
if (ele.matches ("[0-9]+")) Right (ele.toInt) else (Left(ele))).toSeq
// res308: scala.collection.immutable.Seq[Product with Serializable with scala.util.Either[String,Int]] = List(Right(3), Right(4), Right(55))

Scala: filter multiple elements of a list

Assuming I have these scala classes:
Task(id: String, pr: List[PhysicalResource])
PhysicalResource(id: String)
A list: List[PhysicalResource] with these elements:
("PR1" :: "PR2" :: "PR3" :: "PR4" :: Nil)
And this obj: Task object:
("T1", List(PhysicalResource("PR1"), PhysicalResource("PR3")))
I want to return the first two elements (two, because obj.physicalResources.size = 2) of list that match the Physical Resources of the object.
In this example I want to return:
("PR1" :: "PR3" :: Nil)
I am doing this, but it doesn't return anything:
list.filter{x => obj.physicalResources.contains(x)}
Does anyone know how can I do this?
I can't reproduce your issue using filter:
val resources: List[String] =
("PR1" :: "PR2" :: "PR3" :: "PR4" :: Nil)
// Representing obj.physicalResources
val physicalResources: List[String] =
("PR1" :: "Something Else" :: "PR3" :: Nil)
val filtered: List[String] =
resources.filter{x => physicalResources.contains(x)}
println(filtered)
// Prints List(PR1, PR3)
But try this way that uses intersect:
// Take the intersection of the 2 lists; the parts that are the same
println(resources.intersect(physicalResources))
// Prints List(PR1, PR3)
Rolling your own method to accomplish this seems a little excessive.
I recently came up with a generic solution for a similar problem:
def hasSubsequence[A](sup: List[A], sub: List[A]): Boolean = {
def hasSubsequenceR[A](sup1: List[A], sub1: List[A]): Boolean = sup1 match {
case x :: xs => sub1 match {
case y :: ys if x == y => hasSubsequenceR(xs, ys)
case Nil => true
case _ => hasSubsequenceR(xs, sub)
}
case _ => false
}
hasSubsequenceR(sup, sub)
}
where sup is your list and sub is your obj.pr
Hope this helps
If I understand your question correctly, you're trying to match obj's PhysicalResource ids against the master list. List of PhysicalResource ids in obj is obj.pr.map(_.id), hence you can use intersect between the two lists to get the matching ids as follows:
case class PhysicalResource(id: String)
case class Task(id: String, pr: List[PhysicalResource])
val list: List[String] = "PR1" :: "PR2" :: "PR3" :: "PR4" :: Nil
val obj: Task = Task("T1", List(PhysicalResource("PR1"), PhysicalResource("PR3")))
list.intersect(obj.pr.map(_.id))
res1: List[String] = List(PR1, PR3)

accessing list.head, deconstruction vs method call

I am trying to learn a bit of Scala and got stuck on a small oddity when as far as I can I can write the same in two supposedly equivalent ways, but one runs and the other does not.
val test_array = Array(1,2,3,4,5,6,7,8,9,10,3,4)
val it = test_array.sliding(2).toList
def iter(lst: List[Array[Int]]): List[Boolean] = lst match {
case h :: Nil => List(false)
case h :: tail => tail.map(x => x.sameElements(lst.head)) ++ iter(tail)
}
if(iter(it).contains(true)) ...
and
val test_array = Array(1,2,3,4,5,6,7,8,9,10,3,4)
val it = test_array.sliding(2).toList
def iter(lst: List[Array[Int]]): List[Boolean] = lst match {
case h :: Nil => List(false)
case h :: tail => tail.map(x => x.sameElements(h)) ++ iter(tail)
}
if(iter(it).contains(true)) ...
The first example runs, the second throws a noSuchMethodError: scala.collection.immutable.$colon$colon.hd$1()
The only difference is how I access head. In one case I use the deconstruction way and the other I use list.head. Why does one run and the other does not?

How to merge selected List entries together in Scala? (i.e. produce a potentially shorter list)

I have a list of text lines, and want to treat any lines ending with '\' as continuing to the next line, i.e. to merge them. The recursive code below does it, but there must be some clever way, similar to map, filter and all?
reduceLeft is close but it only produces a single result, not a modified (and potentially shorter) new list.
Also suggestions on making the code below leaner are welcome.
object TestX extends App {
// Merge lines together if the former ends with '\'.
//
private def mergeLines( list: List[String] ): List[String] = {
def merge( head: String, tail: List[String] ): List[String] = {
if (head.endsWith("\\")) {
val head2= head.dropRight(1)
if (tail.isEmpty) {
head2 :: Nil // or throw an exception on bad input
} else {
merge( head2 + tail.head, tail.tail )
}
} else {
if (tail.isEmpty)
head :: Nil
else
head :: merge( tail.head, tail.tail ) // note: cannot tailrec this
}
}
if (list.isEmpty) {
list
} else {
merge( list.head, list.tail )
}
}
val list = "These two \\" :: "should be joined" :: "but not this." :: Nil
val list2 = mergeLines(list) // any standard easy way to do this? 'list.mergeIf( _.endsWith('\\') )'
println( list2 )
assert( list2.size == 2 )
}
You can write it using foldLeft:
List("a\\", "b", "c").foldLeft(List.empty[String])((xs, x) => xs match {
case Nil => x :: Nil
case _ => if (xs.head.endsWith("\\")) (xs.head.dropRight(1) + x) :: xs.tail else x :: xs
}).reverse
It's probably not the most efficient way (fine for small list, but not for huge) as it use an immutable data structure, a more efficient approach would use a mutable List.
Here are a few tricks that you could use:
#annotation.tailrec
def mergeLines(xs: List[String], out: List[String] = Nil): List[String] = xs match {
case Nil => out.reverse
case x :: Nil => mergeLines(Nil, x :: out)
case x :: y :: rest =>
if (x endsWith """\""") mergeLines(x.init + y :: rest, out)
else mergeLines(y :: rest, x :: out)
}

Filter heterogeneous list for type

I've an initial list that consists in different types of elements and I've to filter it to just take the int and double values.
For example (1 :: "hello" :: 100 :: 3.14 :: ('a'::10::Nil) :: 'c' :: (5,7,'a') :: Nil) should become (1, 100, 3.14, List(10), (5,7))
I'm having trouble coming up with a solution because once the list is passed to a method it becomes a List[Any] type of list and I need to know the type of each element before casting it. It wouldn't be a problem it didn't contain others substructures such as tuples as I could manage something with a pattern matching.
Is it possible somehow to get the specific type of a Any element and to cast it?
As an academic exercise it's rather silly. You should be learning how to avoid situations like this instead of trying to deal with it. Still, bad code can be rather instructive at times.
def intOrDbl(la :List[Any]) :List[Any] = la.flatMap{
case i:Int => List(i)
case d:Double => List(d)
case l:List[_] => List(intOrDbl(l))
case t:Product => val res = intOrDbl(t.productIterator.toList)
res.length match {
case 0 => Nil
case 1 => List(res)
case 2 => List((res(0),res(1)))
case 3 => List((res(0),res(1),res(2)))
case 4 => List((res(0),res(1),res(2),res(3)))
// etc.
}
case _ => Nil
}
val data = 1 :: "hello" :: 100 :: 3.14 :: ('a'::10::Nil) :: 'c' :: (5,7,'a') :: Nil
intOrDbl(data)
//res0: List[Any] = List(1, 100, 3.14, List(10), (5,7))
One choice you have is to put your result type into an ADT. Here is how it might work:
sealed trait IntOrDoubleOrList
case class IntValue(value: Int) extends IntOrDoubleOrList
case class DoubleValue(value: Double) extends IntOrDoubleOrList
case class ListValue(value: List[IntOrDoubleOrList]) extends IntOrDoubleOrList
def filterIntOrDouble(l: List[_]): List[IntOrDoubleOrList] = {
l.collect({
case iv: Int => IntValue(iv)
case dv: Double => DoubleValue(dv)
case lv: List[_] => ListValue(filterIntOrDouble(lv))
})
}
def test(): Unit = {
val origList = (1 :: "hello" :: 100 :: 3.14 :: ('a' :: 10 :: Nil) :: 'c' :: (5, 7, 'a') :: Nil)
val f = filterIntOrDouble(origList)
println(f)
}
Depending on you further needs you may extend the IntOrDoubleOrList trait with some helper methods like foreach(intHandler: Int => Unit, doubleHandler: Double => Unit)