Альтернативный способ реализации метода groupBy в Scala?
Я придумал эту реализацию groupBy
:
object Whatever
{
def groupBy[T](in:Seq[T],p:T=>Boolean) : Map[Boolean,List[T]] = {
var result = Map[Boolean,List[T]]()
in.foreach(i => {
val res = p(i)
var existing = List[T]() // how else could I declare the reference here? If I write var existing = null I get a compile-time error.
if(result.contains(res))
existing = result(res)
else {
existing = List[T]()
}
existing ::= i
result += res -> existing
})
return result
}
}
Но это не кажется очень чешуйчатым ( это то слово, которое я ищу? ) ко мне. Не могли бы вы предложить некоторые улучшения?
EDIT: после того, как я получил "подсказку" о сворачивании, я реализовал ее следующим образом:
def groupFold[T](in:Seq[T],p:T=>Boolean):Map[Boolean,List[T]] = {
in.foldLeft(Map[Boolean,List[T]]()) ( (m,e) => {
val res = p(e)
m(res) = e :: m.getOrElse(res,Nil)
})
}
Что вы думаете?
4 ответа:
Если вы хотите группировать по предикату (т. е. функции
T => Boolean
), то вы, вероятно, просто хотите сделать это:in partition p
Если вы действительно хотите создать из него карту, то:
Опять же, вы можете просто захотеть это упражнение. В этом случае решение сгиба прекрасно.val (t, f) = in partition p Map(true -> t, false -> f)
Вот пример использования
foldLeft
.scala> def group[T, U](in: Iterable[T], f: T => U) = { | in.foldLeft(Map.empty[U, List[T]]) { | (map, t) => | val groupByVal = f(t) | map.updated(groupByVal, t :: map.getOrElse(groupByVal, List.empty)) | }.mapValues(_.reverse) | } group: [T,U](in: Iterable[T],f: (T) => U)java.lang.Object with scala.collection.DefaultMap[U,List[T]] scala> val ls = List(1, 2, 3, 4, 5) ls: List[Int] = List(1, 2, 3, 4, 5) scala> println(group(ls, (_: Int) % 2)) Map(1 -> List(1, 3, 5), 0 -> List(2, 4))
Scala 2.8 предлагает это в стандартной библиотеке:
scala> println(ls.groupBy((_: Int) % 2)) // Built into Scala 2.8. Map(1 -> List(1, 3, 5), 0 -> List(2, 4))
Я бы просто фильтровал дважды.
object Whatever { def groupBy[T](in: Seq[T], p: T => Boolean) : Map[Boolean,List[T]] = { Map( false -> in.filter(!p(_)).toList , true -> in.filter(p(_)).toList ) } }
Маленькая подсказка: используйте складки вычислить результирующий список функциональным / неизменяемым способом.