groupBy 메소드는 Traversable 트레이트의 자식 클래스로, 그루핑할 때 사용한다. 



예시로 빈도 별로 뽑고 싶다면 다음처럼 코드를 실행한다.


val names = List("Jackson", "Peter", "Jax", "Karl", "Jackson",
                 "Jackson", "Jax")
println(names.groupBy(x => x.toString))



람다식 대신 _를 활용할 수 있다.

val names = List("Jackson", "Peter", "Jax", "Karl",
"Jackson", "Jackson", "Jax")
println(names.groupBy(_.toString))



결과는 다음과 같다.


Map(Karl -> List(Karl), Jax -> List(Jax, Jax), Jackson -> List(Jackson, Jackson, Jackson), Peter -> List(Peter))



엘리먼트의 length 별로 그룹핑할 수 있다. groupBy의 결과 타입은 Map이다. 


val names = List("Jackson", "Peter", "Jax", "Karl",
"Jackson", "Jackson", "Jax")
println(names.groupBy(_.length))

결과는 다음과 같다.


Map(5 -> List(Peter), 4 -> List(Karl), 7 -> List(Jackson, Jackson, Jackson), 3 -> List(Jax, Jax))






같은 단어라면 보이지 않도록 distinct 메소드를 사용한다.

val names = List("Jackson", "Peter", "Jax", "Karl",
"Jackson", "Jackson", "Jax")

val unsorted = names.distinct.groupBy(_.length)
println(unsorted)


결과는 다음처럼 깔끔해졌다.


Map(5 -> List(Peter), 4 -> List(Karl), 7 -> List(Jackson), 3 -> List(Jax))





하지만 length 별로 sorting이 되어 있지 않다. 이를 sort해보자.

이를 위해서는 바로 변환이 안된다. 먼저 Seq(ArrayBuffer)로 변환한 후, 다시 map으로 만든다. 



Map을 Seq로 변환한후 sort한다. sort할 때 sortBy를 써서 1 번째 값(length)로 sort가 되게 한다.

sortBy 말고도 sortWith도 사용할 수 있다. sortWith는 자바와 compare 메소드와 비슷하다. 


그리고, _*를 사용해서 Seq를 ListMap으로 변환한다. 

val names = List("Jackson", "Peter", "Jax", "Karl",
"Jackson", "Jackson", "Jax")
val unsorted = names.distinct.groupBy(_.length)
println(unsorted)

//val sorted = unsorted.toSeq.sortBy(_._1)
val sorted = unsorted.toSeq.sortWith(_._1 < _._1)
println(sorted)

val map = ListMap(sorted:_*)
println(map)



결과는 다음과 같다.


Map(5 -> List(Peter), 4 -> List(Karl), 7 -> List(Jackson), 3 -> List(Jax))

ArrayBuffer((3,List(Jax)), (4,List(Karl)), (5,List(Peter)), (7,List(Jackson)))

Map(3 -> List(Jax), 4 -> List(Karl), 5 -> List(Peter), 7 -> List(Jackson))












마법의 키 sorted:_*가 궁금할 것이다.

sortBy, sortWith의 리턴 타입은 튜플을 엘리먼트로 갖는 Seq[(String, Int)]이다.


실행해보면, 아래와 같은 type mismatch 에러가 발생한다.


val
map = ListMap(sorted)

Error:(26, 23) type mismatch;

 found   : Seq[(Int, List[String])]

 required: (?, ?)

    val map = ListMap(sorted)





따라서, ListMap의 생성자인 apply 메소드를 쫓아가면, GenMapFactory의 apply 메소드를 만나는데, 튜플의 가변 인자:(A, B)*를 받도록 되어 있다. 그래서 _*로 만들어 낸 것이다.


def apply[A, B](elems: (A, B)*): CC[A, B] = (newBuilder[A, B] ++= elems).result()


특이하긴 하지만, scala 공식 문서에도 나와 있는 내용이기도 하다. 

http://docs.scala-lang.org/tutorials/FAQ/finding-symbols.html

f(xs: _*) // Sequence xs is passed as multiple parameters to





다시 groupBy로 넘어와서 case를 쓰는 예시를 사용한다.


val list = List(1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 9)

val g = list.distinct.groupBy {
case num if (num % 2 == 0) => "짝수"
case _ => "홀수"
}


println(g)


결과는 다음과 같다. 


Map(짝수 -> List(2, 4, 6, 8), 홀수 -> List(1, 3, 5, 7, 9))



이번에는 숫자 리스트를 groupBy해서 숫자 엘리먼트별로  몇 개가 존재하는지 살펴본다. 모든 map 값에 _.size를 적용해서 그 키에 대한 새로운 맵을 구성한다. 
val list = List(1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 9)
val g = list.groupBy(x => x).mapValues(_.size)

println(g)
결과는 다음과 같다.

Map(5 -> 1, 1 -> 2, 6 -> 1, 9 -> 3, 2 -> 1, 7 -> 1, 3 -> 1, 8 -> 1, 4 -> 1)



sort를 해보고 싶다면, toSeq.sortBy(_._1):_*를 붙이면 된다.
val list = List(1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 9)
val g = ListMap(list.groupBy(x => x).mapValues(_.size).toSeq.sortBy(_._1):_*)
println(g)

결과는 다음과 같다.



Map(1 -> 2, 2 -> 1, 3 -> 1, 4 -> 1, 5 -> 1, 6 -> 1, 7 -> 1, 8 -> 1, 9 -> 3)



Posted by '김용환'
,