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.
精彩评论