diff --git a/haskell/08-statistics/statistics.hs b/haskell/08-statistics/statistics.hs index b7b03fc..804f73b 100644 --- a/haskell/08-statistics/statistics.hs +++ b/haskell/08-statistics/statistics.hs @@ -1,17 +1,63 @@ -import Data.List (nub, sortOn) +import Data.Function (on) +import Data.List (groupBy, nub, sortOn) import System.IO -- separator -> Input -> Separated Input --- split :: Char -> String -> [String] +split :: Char -> String -> [String] +split _ "" = [] +split sep str = + let (word, rest) = break (== sep) str + in word : case rest of + [] -> [] + (_ : xs) -> split sep xs --- data Entry = +-- Data structure for a student entry +data Entry = Entry + { firstName :: String, + lastName :: String, + subject :: String, + scores :: [Int] + } + deriving (Show) --- averages :: [Entry] -> [(String, Float)] +-- Parse CSV content into entries +parse :: String -> [Entry] +parse content = + let lns = lines content + (header : rows) = lns + in map parseRow rows + where + parseRow line = + let fields = split ',' line + [fn, ln, subj] = take 3 fields + pts = map read (drop 3 fields) :: [Int] + in Entry fn ln subj pts --- content -> Entries --- parse :: String -> [Entry] +-- Calculate averages per subject +averages :: [Entry] -> [(String, Float)] +averages entries = + let grouped = groupBy ((==) `on` subject) (sortOn subject entries) + avgPerSubject grp = + let subj = subject (head grp) + allScores = concat [scores e | e <- grp] + avg = fromIntegral (sum allScores) / fromIntegral (length allScores) + in (subj, avg) + in map avgPerSubject grouped + +-- Find the best student (most accumulated points) +bestStudent :: [Entry] -> (String, Int) +bestStudent entries = + let best = maximum [(sum (scores e), firstName e ++ " " ++ lastName e) | e <- entries] + in (snd best, fst best) main :: IO () main = do putStrLn "Enter a filename: " - putStrLn "..." + filename <- getLine + content <- readFile filename + let entries = parse content + (name, points) = bestStudent entries + avgs = averages entries + putStrLn $ "Best student: " ++ name ++ " (" ++ show points ++ " points)" + putStrLn "Average points per subject:" + mapM_ (\(subj, avg) -> putStrLn $ " " ++ subj ++ ": " ++ show avg) avgs