Reputation: 125
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?
Upvotes: 0
Views: 125
Reputation: 51271
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))
Upvotes: 2
Reputation: 23788
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)
Upvotes: 1