Challenge: find the top three most frequently used words in a string. Only imports from the base
package are allowed (source: codewars)
I'm looking for feedback on (1) readability, and (2) performance
- Was folding the string into "frequency map" a good choice?
- I have several small functions that I compose together. Are these abstraction layers helping or hurting readability?
- How are my functions names?
- Does the
base
package have any libraries that would make this code significantly easier to write? - Could I benefit from using more pointfree style?
import qualified Data.Map as Map
import Data.List
import Data.Function
import Data.Char
import Data.Maybe
top3 :: [Char] -> [[Char]]
top3 str =
let
wordFrequencyMap = foldr (Map.alter increment) Map.empty (normalizedWords str)
sortedWordFrequencies = sortBy reverseBySecond (Map.toList wordFrequencyMap)
in map fst (take 3 sortedWordFrequencies)
where
normalizedWords = filter containsAtLeastOneAlphaNumericChar . lowerCaseWords . map isLegalChar
containsAtLeastOneAlphaNumericChar w = find isAlphaNum w /= Nothing
lowerCaseWords = map (map toLower) . words
isLegalChar c = if isAlphaNum c || c =='\'' then c else ' '
reverseBySecond x y = if snd x < snd y then GT else LT
increment Nothing = Just 1
increment (Just x) = Just (x + 1)