2012-02-26 2 views
1

메시지 클래스 :사용자 이름으로 메시지를 그룹화하는 방법은 무엇입니까?

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"))) 

사용자 경우, 의미 여러 명의 메시지를 게시 한 다음 다른 사용자가 게시 할 때까지 그룹화하십시오. 명령서는 보관해야합니다.

+0

API를 검토해 보셨습니까? 'List'를보고'groupBy'라는 메시지가 있습니다 ... – ziggystar

+3

@ziggystar,'groupBy'는 사용자 "aaa"의 모든 메시지를 함께 그룹화하기 때문에 여기에서 사용할 수 없습니다. 이것은 내가 원하는 것이 아닙니다. – Freewind

+0

죄송합니다, 마지막 문장을 읽지 않았습니다. – ziggystar

답변

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)는 무엇을 당신을 제공합니다 필요.

+0

고마워요.하지만, 코드가 너무 복잡해서 이해할 수 없어요. – Freewind

+0

하지만'contGroupBy (list) (_. username)'은 매우 좋습니다. – Freewind

+1

@Freewind : 내 코드를 조금 닦아 준 건 고맙습니다. 내가 생각했던 것보다 빠르게 타이핑하는 것 같았습니다. –

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 
+0

좋은 솔루션, 이해하기 쉽고, 감사합니다! – Freewind

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)입니다.

+0

아주 좋은 해결책이지만, 고맙습니다. – Freewind

+0

안녕하세요 루이지, 기본 옵션을 선택하여 설명해 주시겠습니까? Nil 또는 List()가 작동하거나 더 간결한 선택을하는 데 타협이 있습니까? –

+0

감사합니다 Don,'Nil'은 그것을 넣는 훨씬 더 좋은 방법입니다! 업데이트 됨. –

3

나는 List뿐만 아니라 연산자 표기법으로도 쓰일 수있는 코드를 만들려고했다. 나는이 함께했다 :

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) 
} 

가 사용할 수 있습니다 (목록, Seqs, 배열, ...)과 같이

list groupBySep (_.username) 
+0

++ 전체 C에 대한 읽을 이름 -1의 CanBuildFrom에 대한 좋은 일반적인 솔루션 한 사용 예를 -1에 대한 +1 WOW - 정말 일반적인 솔루션 에 대한 스타일 코드 1 - ----------------------- +1 – Odomontois

+0

@Odomontois : 예, 알았습니다! ;) 코드는 추악합니다. 알아요.하지만 다른면에서는 CanBuildFrom으로 작업 할 때 얻을 수있는 가장 효율적인 방법 일 것입니다. – sschaef

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)) 
} 
관련 문제