LowRez
LowRez

Reputation: 125

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?

Upvotes: 0

Views: 125

Answers (2)

jwvh
jwvh

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

SergGr
SergGr

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

Related Questions