DEV Community

Discussion on: Daily Challenge #167 - Return String As Sorted Blocks

Collapse
 
craigmc08 profile image
Craig McIlwrath

Solution in Haskell.

import Data.List (group, sortBy, intercalate)

between :: (Ord a) => a -> a -> a -> Bool
between lo hi v = v >= lo && v <= hi

compareLetters :: Char -> Char -> Ordering
compareLetters a b
  | setA < setB = LT
  | setA > setB = GT
  | otherwise   = compare a b
  where setOf c = if between 'a' 'z' c then 0
                  else if between 'A' 'Z' c then 1
                  else 2
        setA    = setOf a
        setB    = setOf b

blocks :: [[a]] -> [[a]]
blocks [] = []
blocks xss = let ys  = map head xss
                 yss = filter (not . null) $ map tail xss
             in  ys : blocks yss

blockify :: (Eq a) => ([a] -> [a]) -> [a] -> [[a]]
blockify sort = blocks . group . sort

blockifyString :: String -> String
blockifyString = intercalate "-" . blockify (sortBy compareLetters)

It sorts the input string, then groups it into arrays of each piece (e.g. "abbcddde" -> ["a", "bb", "c", "ddd", "e"]. The blocks method combines corresponding elements of the sub-arrays after grouping (continuing the example -> ["abcde", "bd", "d"]). Then the strings are joined by a hyphen. My least favorite part of my solution is the compareLetters function which determines the order of 2 letters for letters. I couldn't come up with a better way to do this.