Search code examples
oopscalaimplicits

What to do with operations for a specific kind of collection?


In several different places in my application, I need to take a Seq[SalesRow] and return a Map[String,SalesRow], where the string is the name of a country.

I need to use this in several places. For example, I take a list of all SalesRows and get a global breakdown of sales by country. But in other places, I want to break down my sales by month and then by country (so Map[Month,Seq[SalesRow]] becomes Map[Month,Map[String,Seq[SalesRow]]]) - in still other places, I want to break down by day and then by country.

My question is: where do I put the (small) amount of logic that takes a Seq[SalesRow] and returns a map of countries to rows? Right now, I'm putting it in a companion object method, SalesRow.byCountry(rows : Seq[SalesReport]. Is that optimal?

A slightly crazier idea occurred to me, which is to create an implicit conversion from Seq[SalesRow] to EnhancedSalesRowSeq, which has a byCountry instance method. This appeals to me, because the operation is applicable to any sequence of SalesRows.

Is this a good idea?

Is adding the logic to the companion object my best choice, or are there better options?

Thanks.


Solution

  • In case you are not aware the library comes with a groupBy function. Basically given a Seq[SalesRow] it will give you a Map[T, Seq[SalesRow]] based on a function from SalesRow to T.

    So if your function is easy you can easily get a map. I do like your idea of the enhanced seq in conjunction with putting the implicit in the SalesRow companion:

    case class SalesRow(val month:Int, val country:String, 
      val person:String, val amount:Float)
    
    class EnhancedRow(rows: Seq[SalesRow]) {
      def byCountry: Map[String, Seq[SalesRow]] = 
        rows.groupBy(_.country)
      def byMonth: Map[Int, Seq[SalesRow]] = 
        rows.groupBy(_.month)
      def byCountryByMonth: Map[String, Map[Int, Seq[SalesRow]]] = byCountry.mapValues(r => new EnhancedRow(rows).byMonth)
    }
    
    object SalesRow {
      implicit def toEnhanced(rows: Seq[SalesRow]) = new EnhancedRow(rows) 
    }
    
    object Test {
      def main(args:Array[String] = null) {
        val seq: Seq[SalesRow] = // ... fill this
        println(seq.byCountry)
        println(seq.byCountryByMonth)
        // same as:
        println(seq.byCountry.mapValues(_.byMonth))
      }
    }