Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to find the frequency of characters in a string in Haskell?

Tags:

haskell

How can I count the frequency of characters in a string and then output them in sort of a table?

For example, if I input the word "happy" the result would be

h 1  
a 1  
p 2  
y 1  

If this could be ordered in ASCII order too that would be brilliant.

I know I need to use the count function, any other hints would be appreciated.

EDIT: All the answers are brilliant, only I'm such a beginner at Haskell that I don't actually understand what they are doing.

like image 773
Hagrid123 Avatar asked Aug 18 '11 13:08

Hagrid123


4 Answers

The simplest solution is to use a Data.Map to store the intermediate mapping from character to frequency. You can then construct the counts easily using fromListWith. Since Data.Map is sorted, you get them in ASCII order for free.

λ> :m + Data.Map
λ> let input = "happy"
λ> toList $ fromListWith (+) [(c, 1) | c <- input]
[('a',1),('h',1),('p',2),('y',1)]

So what's happening here?

The idea is to build a Data.Map (a tree map) using the characters as keys and the frequencies as values.

First, we take the input string and make tuples of each character with a 1 to indicate one occurrence.

λ> [(c, 1) | c <- input]
[('h',1),('a',1),('p',1),('p',1),('y',1)]

Next, we use fromListWith to build a sorted map from these key-value pairs by repeatedly inserting each key-value pair into a map. We also give it a function which will be used when a key was already in the map. In our case, we use (+) so that when a character is seen multiple times, we add the count to the existing sum.

Finally we covert the map back into a list of key-value tuples using toList.

like image 65
hammar Avatar answered Sep 22 '22 01:09

hammar


There's probably something shorter, but this works:

Prelude> import Data.List
Prelude Data.List> map (\x -> (head x, length x)) $ group $ sort "happy"
[('h',1),('a',1),('p',2),('y',1)]
like image 37
Michael Kohl Avatar answered Sep 20 '22 01:09

Michael Kohl


func xs = map (\a -> (head a, length a)) $ group $ sort xs

like image 41
Mariy Avatar answered Sep 21 '22 01:09

Mariy


Use list comprehension, no need for any imports or sorting.

[ (x,c) | x<-['A'..'z'], let c = (length.filter (==x)) "happy", c>0 ]

Result:

[('a',1),('h',1),('p',2),('y',1)]

Above is the filtered and rewritten (only character with count > 0) from:

[(x,(length.filter (==x)) "happy" ) | x<-['A'..'z']]

Explanation:

  • Make a list of all characters that match a given character (A..z).
  • For each character, count this list (==length)
  • Put this count in a tuple with the character
like image 37
Rob Audenaerde Avatar answered Sep 22 '22 01:09

Rob Audenaerde