|
module Main where |
|
|
|
import Data.List |
|
import Data.Map (Map) |
|
import qualified Data.Map as Map |
|
import Data.Maybe (listToMaybe, maybe) |
|
import Data.Ord (comparing) |
|
import System.Environment (getArgs) |
|
|
|
data ReportEntry = ReportEntry |
|
{ name :: String |
|
, miles :: Int |
|
, mph :: Maybe Int |
|
} |
|
|
|
type Report = [ReportEntry] |
|
|
|
data CommandEntry |
|
= DriverEntry String |
|
| TripEntry { driverName :: String |
|
, start :: String |
|
, end :: String |
|
, tripMiles :: String } |
|
|
|
type CommandEntryMap = Map String (Int, Float) |
|
|
|
parseLine :: [String] -> CommandEntry |
|
parseLine ("Driver":name:_) = DriverEntry name |
|
parseLine ("Trip":name:start:end:miles:_) = TripEntry name start end miles |
|
|
|
parse :: String -> [CommandEntry] |
|
parse file = parseLine . words <$> lines file |
|
|
|
generateReport :: [CommandEntry] -> Report |
|
generateReport = |
|
(reverse . sortBy (comparing name)) <$> |
|
((map . toRepEntry . getTrips) <*> getNames) |
|
|
|
formatRepEntry :: ReportEntry -> String |
|
formatRepEntry (ReportEntry name miles mph) = |
|
name ++ |
|
": " ++ |
|
show miles ++ " miles" ++ maybe "" ((" @ " ++) . (++ " mph") . show) mph |
|
|
|
toRepEntry :: CommandEntryMap -> String -> ReportEntry |
|
toRepEntry trips name = toRepEntry' $ Map.lookup name trips |
|
where |
|
toRepEntry' :: Maybe (Int, Float) -> ReportEntry |
|
toRepEntry' (Just (minutes, miles)) = |
|
ReportEntry |
|
name |
|
(round miles) |
|
(Just $ round $ miles / (fromIntegral minutes / 60)) |
|
toRepEntry' Nothing = ReportEntry name 0 Nothing |
|
|
|
getNames :: [CommandEntry] -> [String] |
|
getNames [] = [] |
|
getNames (DriverEntry name:xs) = name : getNames xs |
|
getNames (_:xs) = getNames xs |
|
|
|
getTrips :: [CommandEntry] -> CommandEntryMap |
|
getTrips [] = Map.empty |
|
getTrips ((TripEntry name start end miles):xs) = |
|
Map.insertWith |
|
(\(t, m) (t', m') -> (t + t', m + m')) |
|
name |
|
(differenceInMinutes start end, (read miles :: Float)) |
|
(getTrips xs) |
|
getTrips (_:xs) = getTrips xs |
|
|
|
differenceInMinutes :: String -> String -> Int |
|
differenceInMinutes start end = toMinutes end - toMinutes start |
|
|
|
toMinutes :: String -> Int |
|
toMinutes (h:h':_:m:m':_) = |
|
(read (h : h' : []) :: Int) * 60 + (read (m : m' : []) :: Int) |
|
|
|
main :: IO () |
|
main = |
|
listToMaybe <$> getArgs >>= \file -> |
|
case file of |
|
Just file' -> do |
|
entries <- parse <$> readFile file' |
|
mapM_ putStrLn (map formatRepEntry $ generateReport entries) |
|
Nothing -> putStrLn "File does not exist" |