如何按用户名分组邮件?

时间:2012-02-26 13:52:46

标签: scala

消息类:

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 个答案:

答案 0 :(得分:3)

我想不出用提供的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)可以为您提供所需内容。

答案 1 :(得分:3)

我试图创建这样一个代码,它不仅可以用于列表,而且可以用运算符表示法编写。我想出了这个:

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)

答案 2 :(得分:2)

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)

答案 3 :(得分:1)

(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

答案 4 :(得分:1)

这是使用模式匹配和尾递归的另一种方法。由于同时使用了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))
}