import Data.List (groupBy, sortBy, intercalate) type Item = (Int, String) type ItemList = [Item] type ItemGroups = [ItemList] type RankItem a = (a, Int, String) type RankItemList a = [RankItem a] -- make sure the input is ordered and grouped by score prepare :: ItemList -> ItemGroups prepare = groupBy gf . sortBy (flip compare) where gf (a, _) (b, _) = a == b -- give an item a rank rank :: Num a => a -> Item -> RankItem a rank n (a, b) = (n, a, b) -- ranking methods standard, modified, dense, ordinal :: ItemGroups -> RankItemList Int standard = ms 1 where ms _ [] = [] ms n (x:xs) = (rank n <$> x) ++ ms (n + length x) xs modified = md 1 where md _ [] = [] md n (x:xs) = let l = length x nl = n + l nl1 = nl - 1 in (rank nl1 <$> x) ++ md (n + l) xs dense = md 1 where md _ [] = [] md n (x:xs) = map (rank n) x ++ md (n + 1) xs ordinal = zipWith rank [1 ..] . concat fractional :: ItemGroups -> RankItemList Double fractional = mf 1.0 where mf _ [] = [] mf n (x:xs) = let l = length x o = take l [n ..] ld = fromIntegral l a = sum o / ld in map (rank a) x ++ mf (n + ld) xs -- sample data test :: ItemGroups test = prepare [ (44, "Solomon") , (42, "Jason") , (42, "Errol") , (41, "Garry") , (41, "Bernard") , (41, "Barry") , (39, "Stephen") ] -- print rank items nicely nicePrint :: Show a => String -> RankItemList a -> IO () nicePrint xs items = do putStrLn xs mapM_ np items putStr "\n" where np (a, b, c) = putStrLn $ intercalate "\t" [show a, show b, c] main :: IO () main = do nicePrint "Standard:" $ standard test nicePrint "Modified:" $ modified test nicePrint "Dense:" $ dense test nicePrint "Ordinal:" $ ordinal test nicePrint "Fractional:" $ fractional test