¿Qué hacer con las operaciones para un tipo específico de colección?
Pregunta
En varios lugares diferentes de mi aplicación, necesito tomar un Seq[SalesRow]
y devolver un Map[String,SalesRow]
, donde la cadena es el nombre de un país.
Necesito usar esto en varios lugares. Por ejemplo, tomo una lista de todas las sesions y obtengo un desglose global de las ventas por país. Pero en otros lugares, quiero desglosar mis ventas por mes y luego por país (así que Map[Month,Seq[SalesRow]]
convertirse en Map[Month,Map[String,Seq[SalesRow]]]
) - En otros lugares, quiero romper por día y luego en país.
Mi pregunta es: ¿dónde pongo la (pequeña) cantidad de lógica que toma una Seq[SalesRow]
¿Y devuelve un mapa de países a las filas? En este momento, lo estoy poniendo en un método de objeto complementario, SalesRow.byCountry(rows : Seq[SalesReport]
. ¿Eso es óptimo?
Se me ocurrió una idea un poco más loca, que es crear una conversión implícita de Seq[SalesRow]
a EnhancedSalesRowSeq
, que tiene un byCountry
Método de instancia. Esto me atrae, porque la operación es aplicable a cualquier secuencia de vajeros.
¿Es esta una buena idea?
¿Agregar la lógica al objeto complementario es mi mejor opción o hay mejores opciones?
Gracias.
Solución
En caso de que no sepa, la biblioteca viene con un groupBy
función. Básicamente dado un Seq[SalesRow]
te dará un Map[T, Seq[SalesRow]]
basado en una función de SalesRow
a T
.
Entonces, si su función es fácil, puede obtener fácilmente un mapa. Me gusta tu idea del SEQ mejorado junto con poner lo implícito en el SalesRow
compañero:
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))
}
}
Otros consejos
Si el rendimiento no es su principal preocupación, puede poner la lógica en:
class RichTraversable[A](t: Traversable[A]) {
def toMapBy[B](f: A => B): Map[B,A] = t.map{ e => (f(e),e) }.toMap
}
Entonces, si una conversión implícita, puede girar cada Seq
en un Map
con un miembro siendo la clave.
Te sugiero que hagas clases de encapsulación:
case class AllSalesTable(rows: Seq[SalesRow]) {
def toSalesByCountry: SalesByCountry
}
case class ContrySalesTable(rows: Seq[SalesRow])
case class SalesByCountry(map: Map[String, CountrySalesTable])
Tener un lugar para poner el método es un beneficio, pero otro beneficio es que tendrá una mayor seguridad de tipo a costa de un simple ".rows
" aquí y allá.