SamW
SamW

Reputation: 77

How to seperate Seq of super type into Seqs of subtype

Let's say I have this kind of hierarchy for my Data:

sealed trait Foolike
case class Foo extends Foolike
case class Bar extends Foolike
case class Baz extends Foolike

Now I want to create an API where you can put in any of these arguments like this:

def apiMethod(modifiers: Foolike*) = {


  val foos = modifiers
    .filter(_.isInstanceOf[Foo])
    .map(_.asInstanceOf[Foo])

  val bars = modifiers
    .filter(_.isInstanceOf[Bar])
    .map(_.asInstanceOf[Bar])

}

Is there a better way to extract all the Foos and all the Bars from the modifiers?

Upvotes: 3

Views: 324

Answers (3)

insan-e
insan-e

Reputation: 3922

def apiMethod(modifiers: Foolike*) = {

    @tailrec
    def foosAndBarsAndBazs(mods: List[Foolike], foos: List[Foo], bars: List[Bar], bazs: List[Baz]): (List[Foo], List[Bar], List[Baz]) = mods match {
        case Nil => (foos, bars, bazs)
        case (foo: Foo) :: tail => foosAndBarsAndBazs(tail, foo :: foos, bars, bazs)
        case (bar: Bar) :: tail => foosAndBarsAndBazs(tail, foos, bar :: bars, bazs)
        case (baz: Baz) :: tail => foosAndBarsAndBazs(tail, foos, bars, baz :: bazs)
    }

    val (foos, bars, bazs) = foosAndBarsAndBazs(modifiers.toList, Nil, Nil, Nil)        
}

This is a tail recursive solution. Of course, you could have just map over modifiers and pattern match to get a tuple of Options and then flatten them, but then you'd traverse each list one more time...

Upvotes: 3

Nyavro
Nyavro

Reputation: 8866

You can use fold for this:

modifiers.foldRight((List[Foo](), List[Bar]())) {
   case (x:Foo, (foos, bars)) => (x::foos, bars)
   case (x:Bar, (foos, bars)) => (foos, x::bars)
   case (_, acc) => acc
}

Upvotes: 2

Sergii Lagutin
Sergii Lagutin

Reputation: 10661

I'm not sure if I understand your purpose but you can act like this:

def apiMethod(modifiers: Foolike*) = new {
  private val grouped = modifiers.groupBy(_.getClass)
    .withDefaultValue(Nil)

  def get[T <: Foolike](implicit m: Manifest[T]): Seq[T] = {
    val clz = m.runtimeClass.asInstanceOf[Class[_ <: Foolike]]
    grouped(clz).map(_.asInstanceOf[T])
  }
}

val result = apiMethod(Foo(), Bar(), Baz(), Bar())
result.get[Foo] // res0: Seq[Foo] = ArrayBuffer(Foo())
result.get[Bar] // res1: Seq[Bar] = ArrayBuffer(Bar(), Bar())

Upvotes: 2

Related Questions