Как группировать сообщения по имени пользователя? - PullRequest
1 голос
/ 26 февраля 2012

Класс сообщения:

case class Message(username:String, content:String)

Список сообщений:

val list = List(
    Message("aaa", "111"), 
    Message("aaa","222"), 
    Message("bbb","333"),
    Message("aaa", "444"),
    Message("aaa", "555"))

Как сгруппировать сообщения по имени и получить следующий результат:

List( "aaa"-> List(Message("aaa","111"), Message("aaa","222")), 
      "bbb" -> List(Message("bbb","333")),
      "aaa" -> List(Message("aaa","444"), Message("aaa", "555")) )

Это означает, что если пользователь публикует несколько сообщений, группирует их, пока другой пользователь не разместит. Заказ должен быть сохранен.

Ответы [ 5 ]

3 голосов
/ 26 февраля 2012

Я попытался создать такой код, который работает не только со списками и может быть записан в нотации оператора. Я придумал это:

object Grouper {
  import collection.generic.CanBuildFrom

  class GroupingCollection[A, C, CC[C]](ca: C)(implicit c2i: C => Iterable[A]) {
    def groupBySep[B](f: A => B)(implicit
        cbf: CanBuildFrom[C,(B, C),CC[(B,C)]],
        cbfi: CanBuildFrom[C,A,C]
    ): CC[(B, C)] =
      if (ca.isEmpty) cbf().result
      else {
        val iter = c2i(ca).iterator
        val outer = cbf()
        val inner = cbfi()
        val head = iter.next()
        var olda = f(head)

        inner += head
        for (a <- iter) {
          val fa = f(a)
          if (olda != fa) {
            outer += olda -> inner.result
            inner.clear()
          }
          inner += a
          olda = fa
        }
        outer += olda -> inner.result
        outer.result
      }    
  }
  implicit def GroupingCollection[A, C[A]](ca: C[A])(
      implicit c2i: C[A] => Iterable[A]
  ): GroupingCollection[A, C[A], C] =
    new GroupingCollection[A, C[A],C](ca)(c2i)
}

Может использоваться (со списками, последовательностями, массивами, ...) как:

list groupBySep (_.username)
3 голосов
/ 26 февраля 2012

Я не могу придумать простой способ сделать это с помощью предоставленных Seq методов, но вы можете написать свой довольно лаконично и кратко:

def contGroupBy[A, B](s: List[A])(p: A => B) = (List.empty[(B, List[A])] /: s) {
  case (((k, xs) :: rest), y) if k == p(y) => (k, y :: xs) :: rest
  case (acc, y) => (p(y), y :: Nil) :: acc
}.reverse.map { case (k, xs) => (k, xs.reverse) }

Теперь contGroupBy(list)(_.username) дает вамчто ты хочешь.

2 голосов
/ 26 февраля 2012
def group(lst: List[Message], out: List[(String, List[Message])] = Nil)
                                 : List[(String, List[Message])] = lst match {
  case Nil => out.reverse
  case Message(u, c) :: xs => 
    val (same, rest) = lst span (_.username == u)
    group(rest, (u -> same) :: out)
}

Хвостовая рекурсивная версия. Использование просто group(list).

1 голос
/ 26 февраля 2012

Вот еще один метод, использующий сопоставление с образцом и хвостовую рекурсию. Вероятно, не так эффективно, как описанные выше, хотя из-за использования takeWhile и dropWhile.

def groupBy(msgs: List[Message]): List[(String,List[Message])] = msgs match {     
    case Nil => List()
    case head :: tail => (head.username -> 
        (head :: tail.takeWhile(m => m.username == head.username))) +:  
         groupBy(tail.dropWhile(m => m.username == head.username))
}
1 голос
/ 26 февраля 2012
(List[Tuple2[String,List[Message]]]() /: list) {
  case (head :: tail, msg) if msg.username == head._1 =>
    (msg.username -> (msg :: head._2)) :: tail
  case (xs, msg) =>
    (msg.username -> List(msg)) :: xs
} map { t => t._1 -> t._2.reverse } reverse
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...