def makeMap[T](items: Iterable[Item])(extractKey: Item => T): Map[T, Iterable[Item]] =
items.groupBy(extractKey)
Итак, учитывая этот пример Item
class:
case class Item(month: String, itemType: String, quantity: Int, description: String)
Вы можете иметь (я считаю, что типовые описания обязательны):
val byMonth = makeMap[String](items)(_.month)
val byType = makeMap[String](items)(_.itemType)
val byQuantity = makeMap[Int](items)(_.quantity)
val byDescription = makeMap[String](items)(_.description)
Обратите внимание, что _.month
, например, создает функцию, принимающую Item
, что приводит к String
, содержащемуся в поле month
(немного упрощает).
Вы можете, если хотите, сохранить используемые функции для извлечения ключей в сопутствующем объекте:
object Item {
val month: Item => String = _.month
val itemType: Item => String = _.itemType
val quantity: Item => Int = _.quantity
val description: Item => String = _.description
// Allows us to determine if using a predefined extractor or using an ad hoc one
val extractors: Set[Item => Any] = Set(month, itemType, quantity, description)
}
Затем вы можете передавать их так:
val byMonth = makeMap[String](items)(Item.month)
Единственное реальное семантическое изменение заключается в том, что вы явно избегаете возможного дополнительного конструирования лямбд. во время выполнения за счет того, что лямбды остаются в памяти все время. Дополнительным преимуществом является то, что вы можете кэшировать карты с помощью экстрактора, если вы уверены, что источник Item
никогда не меняется: для лямбд - равенство - это ссылочное равенство. Это может быть особенно полезно, если у вас есть некоторый класс, представляющий коллекцию Item
s, а не просто стандартный набор, например:
object Items {
def makeMap[T](items: Iterable[Item])(extractKey: Item => T): Map[T,
Iterable[Item]] =
items.groupBy(extractKey)
}
class Items(val underlying: immutable.Seq[Item]) {
def makeMap[T](extractKey: Item => T): Map[T, Iterable[Item]] =
if (Item.extractors.contains(extractKey)) {
if (extractKey == Item.month) groupedByMonth.asInstanceOf[Map[T, Iterable[Item]]]
else if (extractKey == Item.itemType) groupedByItemType.asInstanceOf[Map[T, Iterable[Item]]]
else if (extractKey == Item.quantity) groupedByQuantity.asInstanceOf[Map[T, Iterable[Item]]]
else if (extractKey == Item.description) groupedByDescription.asInstanceOf[Map[T, Iterable[Item]]]
else throw new AssertionError("Shouldn't happen!")
} else {
Items.makeMap(underlying)(extractKey)
}
lazy val groupedByMonth = Items.makeMap[String](underlying)(Item.month)
lazy val groupedByItemType = Items.makeMap[String](underlying)(Item.itemType)
lazy val groupedByQuantity = Items.makeMap[Int](underlying)(Item.quantity)
lazy val groupedByDescription = Items.makeMap[String](underlying)(Item.description)
}
(это почти наверняка личный рекорд для asInstanceOf
s в небольшом блоке кода ... Я не уверен, должен ли я гордиться или стыдиться этого фрагмента)