开发者

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

开发者 https://www.devze.com 2023-03-02 13:09 出处:网络
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.

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开发者_JAVA百科[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.


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))
  }
}


If performance isn´t your main concern, you can put the logic at:

class RichTraversable[A](t: Traversable[A]) {
  def toMapBy[B](f: A => B): Map[B,A] = t.map{ e => (f(e),e) }.toMap
}

So if an implicit conversion you can turn every Seq into a Map with a member being the key.


I suggest you make encapsulating classes:

case class AllSalesTable(rows: Seq[SalesRow]) {
  def toSalesByCountry: SalesByCountry
}

case class ContrySalesTable(rows: Seq[SalesRow])

case class SalesByCountry(map: Map[String, CountrySalesTable])

Having someplace to put the method is one benefit, but another benefit is that you'll have greater type safety at the cost of a simple ".rows" here and there.

0

精彩评论

暂无评论...
验证码 换一张
取 消