Что делать с операциями для определенного вида коллекции?

В нескольких разных местах моего приложения мне нужно взять Seq[SalesRow] и вернуть Map[String,SalesRow], где строка — это название страны.

Мне нужно использовать это в нескольких местах. Например, я беру список всех SalesRows и получаю глобальную разбивку продаж по странам. Но в других местах я хочу разбить свои продажи по месяцам, а затем по странам (так что Map[Month,Seq[SalesRow]] становится Map[Month,Map[String,Seq[SalesRow]]]) — в третьих местах я хочу разбить свои продажи по дням, а затем по странам.

У меня вопрос: куда мне поместить (небольшое) количество логики, которая принимает Seq[SalesRow] и возвращает карту стран в строки? Прямо сейчас я добавляю его в метод объекта-компаньона, SalesRow.byCountry(rows : Seq[SalesReport]. Это оптимально?

Мне пришла в голову немного более безумная идея: создать неявное преобразование из Seq[SalesRow] в EnhancedSalesRowSeq, которое имеет метод экземпляра byCountry. Мне это нравится, потому что операция применима к любой последовательности SalesRows.

Это хорошая идея?

Является ли добавление логики к объекту-компаньону лучшим выбором, или есть варианты получше?

Спасибо.


person Bill    schedule 30.04.2011    source источник


Ответы (3)


Если вы не знаете, библиотека поставляется с groupBy. По сути, учитывая Seq[SalesRow], он даст вам Map[T, Seq[SalesRow]] на основе функции от SalesRow до T.

Так что, если ваша функция проста, вы можете легко получить карту. Мне нравится ваша идея расширенной последовательности в сочетании с добавлением неявного в компаньон SalesRow:

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))
  }
}
person huynhjl    schedule 01.05.2011

Если производительность не является вашей главной заботой, вы можете поместить логику по адресу:

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

Итак, при неявном преобразовании вы можете превратить каждый Seq в Map с членом, являющимся ключом.

person Peter Schmitz    schedule 30.04.2011
comment
Правильно, это то, что я предлагаю в предпоследнем абзаце. Мой вопрос: это действительно странная или неожиданная вещь? - person Bill; 01.05.2011
comment
Это более или менее эквивалентно groupBy, который является библиотечным методом. - person huynhjl; 01.05.2011
comment
Это не является неожиданным, поскольку вы можете сделать вывод из комментария @huynhjl, что это мой недосмотр, повторно реализующий библиотечный метод. - person Peter Schmitz; 01.05.2011
comment
Хорошо, мой метод подразумевает уникальность по ключу, поэтому значения представляют собой один элемент B, а не Seq[B] по сравнению с библиотечным методом. - person Peter Schmitz; 01.05.2011

Я предлагаю вам сделать инкапсулирующие классы:

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

case class ContrySalesTable(rows: Seq[SalesRow])

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

Наличие места для размещения метода — это одно из преимуществ, но другое преимущество заключается в том, что вы будете иметь большую безопасность типов за счет простого «.rows» здесь и там.

person Daniel C. Sobral    schedule 01.05.2011
comment
Я согласен с Даниэлем! Мой опыт показывает, что наличие классов или определений типов даже для простых структур данных дает более четкий код, и компилятор часто может помочь. - person Peter Schmitz; 01.05.2011