Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Type based collection partitioning in Scala

Tags:

Given the following data model:

sealed trait Fruit  case class Apple(id: Int, sweetness: Int) extends Fruit  case class Pear(id: Int, color: String) extends Fruit 

I've been looking to implement a segregate basket function which for the given basket of fruits will return separate baskets of apples and pears:

def segregateBasket(fruitBasket: Set[Fruit]): (Set[Apple], Set[Pear])

I've attempted a couple of approaches, but none of them seems to be fitting the bill perfectly. Below are my attempts:

  def segregateBasket1(fruitBasket: Set[Fruit]): (Set[Apple], Set[Pear]) = fruitBasket     .partition(_.isInstanceOf[Apple])     .asInstanceOf[(Set[Apple], Set[Pear])] 

This is the most concise solution I've found, but suffers from explicit type casting via asInstanceOf and is going to be a pain to extend should I decide to add additional types of fruits. Therefore:

  def segregateBasket2(fruitBasket: Set[Fruit]): (Set[Apple], Set[Pear]) = {     val mappedFruits = fruitBasket.groupBy(_.getClass)     val appleSet = mappedFruits.getOrElse(classOf[Apple], Set()).asInstanceOf[Set[Apple]]     val pearSet = mappedFruits.getOrElse(classOf[Pear], Set()).asInstanceOf[Set[Pear]]     (appleSet, pearSet)   } 

Resolves the problem of additional fruit types (extension really easy), but still strongly depends on risky type casting 'asInstanceOf' which I'd rather avoid. Therefore:

  def segregateBasket3(fruitBasket: Set[Fruit]): (Set[Apple], Set[Pear]) = {     val appleSet = collection.mutable.Set[Apple]()     val pearSet = collection.mutable.Set[Pear]()      fruitBasket.foreach {       case a: Apple => appleSet += a       case p: Pear => pearSet += p     }     (appleSet.toSet, pearSet.toSet)   } 

Resolves the problem of explicit casting, but uses mutable collections and ideally I'd like to stick with immutable collections and idiomatic code.

I've looked here: Scala: Filtering based on type for some inspiration, but couldn't find a better approach either.

Does anyone have any suggestions on how this functionality can be better implemented in Scala?

like image 693
Norbert Radyk Avatar asked Jun 13 '14 16:06

Norbert Radyk


People also ask

What does .partition do in Scala?

partition() method is a member of TraversableLike trait, it is used to run a predicate method on each elements of a collection. It returns two collections, one collection is of elements which satisfiles a given predicate function and another collection is of elements which do not satisfy the given predicate function.

Does Scala list preserve order?

Lists preserve order, can contain duplicates, and are immutable.


1 Answers

  val emptyBaskets: (List[Apple], List[Pear]) = (Nil, Nil)    def separate(fruits: List[Fruit]): (List[Apple], List[Pear]) = {     fruits.foldRight(emptyBaskets) { case (f, (as, ps)) =>       f match {         case a @ Apple(_, _) => (a :: as, ps)         case p @ Pear(_, _)  => (as, p :: ps)       }     }   } 
like image 146
nlim Avatar answered Oct 05 '22 19:10

nlim