Reputation: 1665
How do I efficiently count all occurences of each element in a list? I thought of using an associative list or some hash map, but immutability gets in the way and it's not evidently clear how an (hopefully) elegant solution should arise.
The signature could be like this:
countOccurences :: [a] -> [(a, Int)]
Example:
countOccurences [1, 1, 2, 3, 1, 2, 4]
results in
[(1, 3), (2, 2), (3, 1), (4, 1)]
(order is not important though).
Upvotes: 3
Views: 1012
Reputation: 16145
Since chi provided a solution using group . sort
, here is one that uses Data.Map
:
import qualified Data.Map.Strict as M
import Data.Map.Strict (Map)
histogram :: Ord a => [a] -> Map a Int
histogram = M.fromListWith (+) . (`zip` [1,1..])
This also uses O(n log n) time.
I thought of using an associative list or some hash map, but immutability gets in the way
Data.Map
is a tree-based associative map, so maybe this representation is for you.
If you'd rather like an [(a, Int)]
, M.assocs
can convert the Data.Map
back:
countOccurrences :: Ord a => [a] -> [(a, Int)]
countOccurrences = M.assocs . histogram
Upvotes: 6
Reputation: 116174
group . sort
will produce an output list such as
> group . sort $ [1, 1, 2, 3, 1, 2, 4]
[[1,1,1],[2,2],[3],[4]]
Hence,
> map (head &&& length) . group . sort $ [1, 1, 2, 3, 1, 2, 4]
[(1,3),(2,2),(3,1),(4,1)]
So, we obtain
import Data.List (group, sort)
import Control.Arrow ((&&&))
countOccurences :: Ord a => [a] -> [(a, Int)]
countOccurences = map (head &&& length) . group . sort
It should require only O(n log n)
time.
Upvotes: 8