[2015-04-06] Challenge #209 [Easy] The Button can be pressed but once...

Here's another Haskell solution, besides the one OP provided. OP reminded me of mapAccumL, which I'd felt the need for while coding this up, but I'd already written my getFlairs function with a subfunction in a where clause that takes a previous time. With the users sorted, you can just subtract the previous user's time from the current one's time, and it works out, because the other, shorter times from earlier in the list are "contained" within the single, previous user's time, so I start by passing in no offset (no previous clicker before the first, when sorted), and then just recurse with whatever the current user's time is.

I'm not in love with my double-reverse for getting the time at the end of each line. That's a hack, but then, I feel like most of the parsing we do in Haskell - before getting to fully thought-out Parsec parsers - is at best hackish. That said, investigating some pain points lead me to readIO, which I really like, and it reminded me of replicateM, which is really handy for collecting a given set of input lines, among other things.

import Control.Monad (replicateM)
import Data.List (sortBy)
import Data.Ord (comparing)

getFlairs :: (RealFrac a, Integral b) => [(String, a)] -> [(String, b)]
getFlairs xs = r 0 (sortBy (comparing snd) xs)
    where r _ [] = []
          r p ((u,t):xs) = (u,floor $ 60-(t-p)) : r t xs

readUserTime :: IO (String, Double)
readUserTime = do
    l <- getLine
    let user = takeWhile (/= ':') l
        time = reverse . takeWhile (/= ' ') $ reverse l
    return (user, read time :: Double)

formatUserTime :: (String, Integer) -> String
formatUserTime (u,t) = u ++ ": " ++ show t

main = do
    n <- readLn :: IO Int
    userTimes <- replicateM n readUserTime
    putStr . ("\n" ++) . unlines . map formatUserTime $ getFlairs userTimes
/r/dailyprogrammer Thread