group by key in haskell

什麼是 Group By Key

Group By Key 就是把 Element 計算一個 key,相同 key 的 element 就放左一組內 例如 group by key by the reminder equal to 3 in Scala

val arr = Array(2, 4, 5, 6, 9, 23, 24, 25, 31, 37)
scala> val ans = arr.groupBy { n => n % 3 }
ans: scala.collection.immutable.Map[Int,Array[Int]] = Map(2 -> Array(2, 5, 23), 1 -> Array(4, 25, 31, 37), 0 -> Array(6, 9, 24))

問題

Haskell 中把 List 中的 element Group By Key 是麻煩的一件事 因為 Data.List.groupBy 是 linear scan 每兩個 element, 然後比較他們是否相同

import Data.List
Prelude Data.List> groupBy (==) [1, 1, 2, 3, 1] 
[[1,1],[2],[3],[1]]

解決方法 1

List 事前需要被 sorted by key Scala 的例子在 Haskell 可以寫成這樣 值得注意 sortBy 要求 data Ordering, 需要用 compare, 不是 Bool 結果的 > == <

Prelude Data.List> let allInOne = groupBy (\x y -> (x `mod` 3) == (y `mod` 3)) . sortBy (\x y -> compare (x `mod` 3) (y `mod` 3))
Prelude Data.List> allInOne [2, 4, 5, 6, 9, 23, 24, 25, 31, 37]
[[6,9,24],[4,25,31,37],[2,5,23]]

解決方法 2

一個 package 解決這個問題, 當中的 groupBy 是 scala 中的 `groupByKey 但 return 一個 List

安裝

> cabal install utility-ht
> ......
> ghci

例子

Prelude> import Data.List.HT
Prelude Data.List.HT> let a = [2, 4, 5, 6, 9, 23, 24, 25, 31, 37]
Prelude Data.List.HT> groupBy (\x y -> x `mod` 3 == y `mod` 3 )
[[2],[4],[5],[6,9],[23],[24],[25,31,37]]