F1 Results with Haskell - list

What is the most simple solution to do this ?
Define a function for calculating the series of Formula 1 race in function "f1Results". The parameter is a list whose elements are lists based on the result of each Grand Prix. Each such list shall contain the names of the competitors listed by the GP result (the winner is the first of the list). The result of the function has to be a list of names of F1 pilots sorted by number of points in the overall F1 season.
f1Results :: [[String]] -> [String]
Points have to be granted for each position like in the list bellow (again the 25 is for the winner) pointsScoring (eg: 1st place 25 points, 2nd place 18 points, etc.).
pointsScoring :: [Int]
pointsScoring = [25, 18, 15, 12, 10, 8, 6, 4, 2, 1]

You have a constant for what the scoring is
scoring :: [Int]
scoring = [25, 18, 15, 12, 10, 8, 6, 4, 2, 1]
Then you need a way for pairing up a driver with the score they got. Whenever you're pairing two things in Haskell, the canonical choice is to use a tuple. The easiest way to construct a list of tuples from two lists is the zip function:
zip :: [a] -> [b] -> [(a, b)]
And in this case can be used to assign scores for a race:
assignScores :: [String] -> [(String, Int)]
assignScores race = zip race scoring
Now, we need a way to total up the scores for a driver for each race. We want to be able to turn something like
[("Bob", 12), ("Joe", 10), ("Bob", 18), ("Joe", 25)]
into
[("Bob", 30), ("Joe", 35)]
The easiest way would be to make a single list of all the scores for all the races
assignAllScores :: [[String]] -> [(String, Int)]
assignAllScores races = concatMap assignScores races
Then we can use sortBy from Data.List to get all the same names next to each other
sortBy :: (a -> a -> Ordering) -> [a] -> [a]
compare :: Ord a => a -> a -> Ordering
sortByDriver :: [(String, Int)] -> [(String, Int)]
sortByDriver races = sortBy (\(n1, s1) (n2, s2) -> compare n1 n2) races
Then we can use groupBy (also from Data.List) to group them all by name
groupBy :: (a -> a -> Bool) -> [a] -> [[a]]
groupByDriver :: [(String, Int)] -> [[(String, Int)]]
groupByDriver races = groupBy (\(n1, s1) (n2, s2) -> n1 == n2) races
But this gives us a list like
[[("Bob", 12), ("Bob", 18)], [("Joe", 10), ("Joe", 25)]]
We now need a way to convert this into the form
[("Bob", [12, 18]), ("Joe", [10, 25])]
where all the scores are aggregated back into a list, and we don't repeat the names at all. This is left as an exercise.
aggregateScores :: [[(String, Int)]] -> [(String, [Int])]
Then we can finally calculate the sum of these scores
sumScores :: [(String, [Int])] -> [(String, Int)]
sumScores races = map (\(name, scores) -> (name, sum scores)) races
Then finally we can sort by the scores to get everyone in order
sortByScore :: [(String, Int)] -> [(String, Int)]
sortByScore races = sortBy (\(n1, s1) (n2, s2) -> compare s2 s1) races
Notice that I have compare s2 s1 instead of compare s1 s2, this means it will be sorted in descending order instead of ascending order.
The last step is to strip out the scores, now we have our list of drivers in order from winner to loser
removeScores :: [(String, Int)] -> [String]
removeScores races = map fst races
So to combine everything together into one function
f1Results :: [[String]] -> [String]
f1Results races =
removeScores $
sortByScore $
sumScores $
aggregateScores $
groupByDriver $
sortByDriver $
assignAllScores races
There are several tricks that can make this code shorter, namely Data.Function.on, Data.Ord.comparing, and a fun operator from Control.Arrow. Don't turn this in as homework, I just wanted to show an alternative that uses less code.
import Data.List
import Data.Function (on)
import Data.Ord (comparing)
scoring :: [Int]
scoring = [25, 18, 15, 12, 10, 8, 6, 4, 2, 1]
f1Results :: [[String]] -> [String]
f1Results =
map fst . sortBy (on (flip compare) snd) .
map ((head *** sum) . unzip) .
groupBy (on (==) fst) . sortBy (comparing fst) .
concatMap (`zip` scoring)
Or using Data.Map:
import Data.Map (assocs, fromListWith)
import Data.List
import Data.Ord (comparing)
scoring :: [Int]
scoring = [25, 18, 15, 12, 10, 8, 6, 4, 2, 1]
f1Results :: [[String]] -> [String]
f1Results =
reverse . map fst . sortBy (comparing snd) .
assocs . fromListWith (+) .
concatMap (`zip` scoring)

One way could be to use a map where the keys are the drivers' names and the values are the scores. To associate scores with drivers for each sublist in f1results, one could zip the sublist with pointsScoring. A function could take each of the zipped sublists and add the score for each driver encountered in (or newly added to) the map. Then sort the map according to the values.
For example,
import qualified Data.Map as M
import Data.List(sortBy)
pointsScoring :: [Int]
pointsScoring = [25, 18, 15, 12]
f1List :: [[String]]
f1List = [["alex","bart","cecyl","dark"]
,["dark","bart","cecyl","alex"]
,["bart","cecyl","alex","dark"]]
f1Results :: [[String]] -> [String]
f1Results list = map fst . sortBy (\(n1,s1) (n2,s2) -> compare s2 s1) . M.toList
$ foldr f M.empty (concatMap (zip pointsScoring) list)
where f (score,name) results = M.insertWithKey updateMap name score results
updateMap key newValue oldValue = newValue + oldValue
Output:
*Main> f1Results f1List
["bart","alex","dark","cecyl"]

Related

Filter a list of tuples and return the first item based on a condition that applies to the second item

The task is as follows: I have a list of tuples containing country names, and the official language. I have another list with "3 item tuples" of translators in (first name, last name, language) format. I need to get a list of the countries, which official language is NOT spoken by any of the translators.
It has to be one call to a function of such form:
foo :: [(String, String)] -> [(String, String, String)] -> [String]
I have tried all sorts of map and filter combinations to no avail. The closest I came was using sets.
third :: (a, b, c) -> c
third (_, _, z) = z
translators x = Set.fromList (map third x)
languages x = Set.fromList (map snd x)
diff x y = Set.toList (Set.difference (languages x) (translators y))
This gives me all the languages that are in the first list but not spoken by the translators. However of course, the task is to give a list of the country names, not the languages. So I tried this as well, but it does not work:
foo x y = filter ((Set.notMember (translators y)).snd) x
I'm a beginner and I would much appreciate some help.
Your first function gives you the correct answer - except it gives you the list of languages. How about an assoc list so you can lookup country associated with the language?
To do that, you just have to swap the country and language in the (Country, Language) tuple
import Data.Tuple ( swap )
langMap :: [(String, String)] -> [(String, String)]
langMap = map swap
Now once you have the result of your own diff function, you just have to map a lookup function over the resulting list
diff :: [(String, String)] -> [(String, String, String)] -> [String]
diff x y = map (fromJust . (`lookup` langMap')) diffList
where
diffList = Set.toList $ Set.difference (languages x) (translators y)
langMap' = langMap x
If you are concerned about performance, you can replace the assoc list by a Data.Map
import qualified Data.Map as Map
langMap :: [(String, String)] -> Map.Map String String
langMap = Map.fromList . map swap
And also change the lookup in diff to Map.lookup.
diff :: [(String, String)] -> [(String, String, String)] -> [String]
diff x y = map (fromJust . (`Map.lookup` langMap')) diffList
where
diffList = Set.toList $ Set.difference (languages x) (translators y)
langMap' = langMap x
However, there's one caveat here - the lookup method will only work if you have only one country associated with each language.
If you want to support the situation when there are multiple countries associated with the same language. You need some extra batteries on the langMap-
langMap :: [(String, String)] -> Map.Map String [String]
langMap x = Map.fromListWith (++) [(v, [k]) | (k, v) <- x]
This is a slight modification of this answer. It creates a map of String and [String]. The key string is the language, and the value is a list of countries
Now, since the value of the map is a list of strings instead of just a string - we also need to change the diff a bit
diff :: [(String, String)] -> [(String, String)] -> [String]
diff x y = concatMap (fromJust . (`Map.lookup` langMap')) diffList
where
diffList = Set.toList $ Set.difference (languages x) (translators y)
langMap' = langMap x
Huh, that was simple. Just changing map to concatMap is enough - it'll flatten the list of list of strings, into just a list of strings.
Let's see it in action-
λ> xs = [("US", "English"), ("Mexico", "Spanish"), ("France", "French"), ("Spain", "Spanish"), ("UK", "English"), ("Italy", "Italian")]
λ> ys = [("Foo", "Spanish"), ("Bar", "French")]
λ> diff xs ys
["UK","US","Italy"]
Let's focus on correctness, on solving the problem, not on the solution being an efficient one. That we can take care of later. Correctness first, efficiency later!
Simplest and most visual way to code this is using List Comprehensions.
foo :: [(String, String)] -> [(String, String, String)] -> [String]
-- Country Language FirstN LastN Language
-- return: list of Contries with no translator
foo cls nnls = [ c | (c, ___) <- cls, -- for each country/language pair,
-- test the {list of ()s for each translator that ...}
___ [ () | (_, _, ___) <- nnls, ___ == ___] ]
Fill in the ___ blanks (you don't have to fill the _ blanks, those are Haskell's anonymous, throwaway variables).
Now you can make it efficient, treating the above as executable specification.

Triangularizing a list in Haskell

I'm interested in writing an efficient Haskell function triangularize :: [a] -> [[a]] that takes a (perhaps infinite) list and "triangularizes" it into a list of lists. For example, triangularize [1..19] should return
[[1, 3, 6, 10, 15]
,[2, 5, 9, 14]
,[4, 8, 13, 19]
,[7, 12, 18]
,[11, 17]
,[16]]
By efficient, I mean that I want it to run in O(n) time where n is the length of the list.
Note that this is quite easy to do in a language like Python, because appending to the end of a list (array) is a constant time operation. A very imperative Python function which accomplishes this is:
def triangularize(elements):
row_index = 0
column_index = 0
diagonal_array = []
for a in elements:
if row_index == len(diagonal_array):
diagonal_array.append([a])
else:
diagonal_array[row_index].append(a)
if row_index == 0:
(row_index, column_index) = (column_index + 1, 0)
else:
row_index -= 1
column_index += 1
return diagonal_array
This came up because I have been using Haskell to write some "tabl" sequences in the On-Line Encyclopedia of Integer Sequences (OEIS), and I want to be able to transform an ordinary (1-dimensional) sequence into a (2-dimensional) sequence of sequences in exactly this way.
Perhaps there's some clever (or not-so-clever) way to foldr over the input list, but I haven't been able to sort it out.
Make increasing size chunks:
chunks :: [a] -> [[a]]
chunks = go 0 where
go n [] = []
go n as = b : go (n+1) e where (b,e) = splitAt n as
Then just transpose twice:
diagonalize :: [a] -> [[a]]
diagonalize = transpose . transpose . chunks
Try it in ghci:
> diagonalize [1..19]
[[1,3,6,10,15],[2,5,9,14],[4,8,13,19],[7,12,18],[11,17],[16]]
This appears to be directly related to the set theory argument proving that the set of integer pairs are in one-to-one correspondence with the set of integers (denumerable). The argument involves a so-called Cantor pairing function.
So, out of curiosity, let's see if we can get a diagonalize function that way.
Define the infinite list of Cantor pairs recursively in Haskell:
auxCantorPairList :: (Integer, Integer) -> [(Integer, Integer)]
auxCantorPairList (x,y) =
let nextPair = if (x > 0) then (x-1,y+1) else (x+y+1, 0)
in (x,y) : auxCantorPairList nextPair
cantorPairList :: [(Integer, Integer)]
cantorPairList = auxCantorPairList (0,0)
And try that inside ghci:
λ> take 15 cantorPairList
[(0,0),(1,0),(0,1),(2,0),(1,1),(0,2),(3,0),(2,1),(1,2),(0,3),(4,0),(3,1),(2,2),(1,3),(0,4)]
λ>
We can number the pairs, and for example extract the numbers for those pairs which have a zero x coordinate:
λ>
λ> xs = [1..]
λ> take 5 $ map fst $ filter (\(n,(x,y)) -> (x==0)) $ zip xs cantorPairList
[1,3,6,10,15]
λ>
We recognize this is the top row from the OP's result in the text of the question.
Similarly for the next two rows:
λ>
λ> makeRow xs row = map fst $ filter (\(n,(x,y)) -> (x==row)) $ zip xs cantorPairList
λ> take 5 $ makeRow xs 1
[2,5,9,14,20]
λ>
λ> take 5 $ makeRow xs 2
[4,8,13,19,26]
λ>
From there, we can write our first draft of a diagonalize function:
λ>
λ> printAsLines xs = mapM_ (putStrLn . show) xs
λ> diagonalize xs = takeWhile (not . null) $ map (makeRow xs) [0..]
λ>
λ> printAsLines $ diagonalize [1..19]
[1,3,6,10,15]
[2,5,9,14]
[4,8,13,19]
[7,12,18]
[11,17]
[16]
λ>
EDIT: performance update
For a list of 1 million items, the runtime is 18 sec, and 145 seconds for 4 millions items. As mentioned by Redu, this seems like O(n√n) complexity.
Distributing the pairs among the various target sublists is inefficient, as most filter operations fail.
To improve performance, we can use a Data.Map structure for the target sublists.
{-# LANGUAGE ExplicitForAll #-}
{-# LANGUAGE ScopedTypeVariables #-}
import qualified Data.List as L
import qualified Data.Map as M
type MIL a = M.Map Integer [a]
buildCantorMap :: forall a. [a] -> MIL a
buildCantorMap xs =
let ts = zip xs cantorPairList -- triplets (a,(x,y))
m0 = (M.fromList [])::MIL a
redOp m (n,(x,y)) = let afn as = case as of
Nothing -> Just [n]
Just jas -> Just (n:jas)
in M.alter afn x m
m1r = L.foldl' redOp m0 ts
in
fmap reverse m1r
diagonalize :: [a] -> [[a]]
diagonalize xs = let cm = buildCantorMap xs
in map snd $ M.toAscList cm
With that second version, performance appears to be much better: 568 msec for the 1 million items list, 2669 msec for the 4 millions item list. So it is close to the O(n*Log(n)) complexity we could have hoped for.
It might be a good idea to craete a comb filter.
So what does comb filter do..? It's like splitAt but instead of splitting at a single index it sort of zips the given infinite list with the given comb to separate the items coressponding to True and False in the comb. Such that;
comb :: [Bool] -- yields [True,False,True,False,False,True,False,False,False,True...]
comb = iterate (False:) [True] >>= id
combWith :: [Bool] -> [a] -> ([a],[a])
combWith _ [] = ([],[])
combWith (c:cs) (x:xs) = let (f,s) = combWith cs xs
in if c then (x:f,s) else (f,x:s)
λ> combWith comb [1..19]
([1,3,6,10,15],[2,4,5,7,8,9,11,12,13,14,16,17,18,19])
Now all we need to do is to comb our infinite list and take the fst as the first row and carry on combing the snd with the same comb.
Lets do it;
diags :: [a] -> [[a]]
diags [] = []
diags xs = let (h,t) = combWith comb xs
in h : diags t
λ> diags [1..19]
[ [1,3,6,10,15]
, [2,5,9,14]
, [4,8,13,19]
, [7,12,18]
, [11,17]
, [16]
]
also seems to be lazy too :)
λ> take 5 . map (take 5) $ diags [1..]
[ [1,3,6,10,15]
, [2,5,9,14,20]
, [4,8,13,19,26]
, [7,12,18,25,33]
, [11,17,24,32,41]
]
I think the complexity could be like O(n√n) but i can not make sure. Any ideas..?

Haskell - Printing a list of tuples

I have a list of tuples in the form:
[(String, Int)]
How can I print this to display like:
String : Int
String : Int
String : Int
...
I am very new to Haskell so please make it as clear as possible. Thank you!
Update: Here's how the code of my program now looks:
main = do
putStrLn "********* Haskell word frequency counter *********"
putStrLn ""
conts <- readFile "text.txt"
let lowConts = map toLower conts
let counted = countAllWords (lowConts)
let sorted = sortTuples (counted)
let reversed = reverse sorted
putStrLn "Word : Count"
mapM_ (printTuple) reversed
-- Counts all the words.
countAllWords :: String -> [(String, Int)]
countAllWords fileContents = wordsCount (toWords (noPunc fileContents))
-- Splits words and removes linking words.
toWords :: String -> [String]
toWords s = filter (\w -> w `notElem` ["and","the","for"]) (words s)
-- Remove punctuation from text String.
noPunc :: String -> String
noPunc xs = [ x | x <- xs, not (x `elem` ",.?!-:;\"\'") ]
-- Counts, how often each string in the given list appears.
wordsCount :: [String] -> [(String, Int)]
wordsCount xs = map (\xs -> (head xs, length xs)) . group . sort $ xs
-- Sort list in order of occurrences.
sortTuples :: [(String, Int)] -> [(String, Int)]
sortTuples sort = sortBy (comparing snd) sort
printTuple :: Show a => [(String, a)] -> IO ()
printTuple xs = forM_ xs (putStrLn . formatOne)
formatOne :: Show a => (String, a) -> String
formatOne (s,i) = s ++ " : " ++ show i
It returns this error to me:
fileToText.hs:18:28:
Couldn't match type ‘(String, Int)’ with ‘[(String, a0)]’
Expected type: [[(String, a0)]]
Actual type: [(String, Int)]
In the second argument of ‘mapM_’, namely ‘reversed’
In a stmt of a 'do' block: mapM_ (printTuple) reversed
Thanks for any help!
let's start by formatting one item:
formatOne :: Show a => (String, a) -> String
formatOne (s,i) = s ++ " : " ++ show i
now you can use this function (for example) with forM_ from Control.Monad to print it to the screen like this (forM_ because we want to be in the IO-Monad - because we are going to use putStrLn):
Prelude> let test = [("X1",4), ("X2",5)]
Prelude> import Control.Monad (forM_)
Prelude Control.Monad> forM_ test (putStrLn . formatOne)
X1 : 4
X2 : 5
in a file you would use it like this:
import Control.Monad (forM_)
printTuples :: Show a => [(String, a)] -> IO ()
printTuples xs = forM_ xs (putStrLn . formatOne)
formatOne :: Show a => (String, a) -> String
formatOne (s,i) = s ++ " : " ++ show i
compiling file
overall here is a version of your code that will at least compile (cannot test it without the text file ;) )
import Control.Monad (forM_)
import Data.Char (toLower)
import Data.List (sort, sortBy, group)
import Data.Ord (comparing)
main :: IO ()
main = do
putStrLn "********* Haskell word frequency counter *********"
putStrLn ""
conts <- readFile "text.txt"
let lowConts = map toLower conts
let counted = countAllWords lowConts
let sorted = sortTuples counted
let reversed = reverse sorted
putStrLn "Word : Count"
printTuples reversed
-- Counts all the words.
countAllWords :: String -> [(String, Int)]
countAllWords = wordsCount . toWords . noPunc
-- Splits words and removes linking words.
toWords :: String -> [String]
toWords = filter (\w -> w `notElem` ["and","the","for"]) . words
-- Remove punctuation from text String.
noPunc :: String -> String
noPunc xs = [ x | x <- xs, x `notElem` ",.?!-:;\"\'" ]
-- Counts, how often each string in the given list appears.
wordsCount :: [String] -> [(String, Int)]
wordsCount = map (\xs -> (head xs, length xs)) . group . sort
-- Sort list in order of occurrences.
sortTuples :: [(String, Int)] -> [(String, Int)]
sortTuples = sortBy $ comparing snd
-- print one tuple per line separated by " : "
printTuples :: Show a => [(String, a)] -> IO ()
printTuples = mapM_ (putStrLn . formatTuple)
where formatTuple (s,i) = s ++ " : " ++ show i
I also removed the compiler warnings and HLINTed it (but skipped the Control.Arrow stuff - I don't think head &&& length is more readable option here)

groupBy with multiple test functions

Is there a better and more concise way to write the following code in Haskell? I've tried using if..else but that is getting less readable than the following. I want to avoid traversing the xs list (which is huge!) 8 times to just separate the elements into 8 groups. groupBy from Data.List takes only one test condition function: (a -> a -> Bool) -> [a] -> [[a]].
x1 = filter (check condition1) xs
x2 = filter (check condition2) xs
x3 = filter (check condition3) xs
x4 = filter (check condition4) xs
x5 = filter (check condition5) xs
x6 = filter (check condition6) xs
x7 = filter (check condition7) xs
x8 = filter (check condition8) xs
results = [x1,x2,x3,x4,x5,x6,x7,x8]
This only traverses the list once:
import Data.Functor
import Control.Monad
filterN :: [a -> Bool] -> [a] -> [[a]]
filterN ps =
map catMaybes . transpose .
map (\x -> map (\p -> x <$ guard (p x)) ps)
For each element of the list, the map produces a list of Maybes, each Maybe corresponding to one of the predicates; it is Nothing if the element does not satisfy the predicate, or Just x if it does satisfy the predicate. Then, the transpose shuffles all these lists so that the list is organised by predicate, rather than by element, and the map catMaybes discards the entries for elements that did not satisfy a predicate.
Some explanation: x <$ m is fmap (const x) m, and for Maybe, guard b is if b then Just () else Nothing, so x <$ guard b is if b then Just x else Nothing.
The map could also be written as map (\x -> [x <$ guard (p x) | p <- ps]).
If you insist on one traversing the list only once, you can write
filterMulti :: [a -> Bool] -> [a] -> [[a]]
filterMulti fs xs = go (reverse xs) (repeat []) where
go [] acc = acc
go (y:ys) acc = go ys $ zipWith (\f a -> if f y then y:a else a) fs acc
map (\ cond -> filter (check cond) xs) [condition1, condition2, ..., condition8]
I think you could use groupWith from GHC.Exts.
If you write the a -> b function to assign every element in xs its 'class', I belive groupWith would split xs just the way you want it to, traversing the list just once.
groupBy doesn't really do what you're wanting; even if it did accept multiple predicate functions, it doesn't do any filtering on the list. It just groups together contiguous runs of list elements that satisfy some condition. Even if your filter conditions, when combined, cover all of the elements in the supplied list, this is still a different operation. For instance, groupBy won't modify the order of the list elements, nor will it have the possibility of including a given element more than once in the result, while your operation can do both of those things.
This function will do what you're looking for:
import Control.Applicative
filterMulti :: [a -> Bool] -> [a] -> [[a]]
filterMulti ps as = filter <$> ps <*> pure as
As an example:
> filterMulti [(<2), (>=5)] [2, 5, 1, -2, 5, 1, 7, 3, -20, 76, 8]
[[1, -2, 1, -20], [5, 5, 7, 76, 8]]
As an addendum to nietaki's answer (this should be a comment but it's too long, so if his answer is correct, accept his!), the function a -> b could be written as a series of nested if ... then .. else, but that is not very idiomatic Haskell and not very extensible. This might be slightly better:
import Data.List (elemIndex)
import GHC.Exts (groupWith)
f xs = groupWith test xs
where test x = elemIndex . map ($ x) $ [condition1, ..., condition8]
It categorises each element by the first condition_ it satisfies (and puts those that don't satisfy any into their own category).
(The documentation for elemIndex is here.)
The first function will return a list of "uppdated" lists and the second function will go through the whole list and for each value uppdate the list
myfilter :: a -> [a -> Bool] -> [[a]] -> [[a]]
myfilter _ [] [] = []
myfilter x f:fs l:ls | f x = (x:l): Myfilter x fs ls
| otherwise = l:Myfilter x fs ls
filterall :: [a] -> [a -> Bool] -> [[a]] -> [[a]]
filterall [] _ l = l
filterall x:xs fl l:ls = filterall xs fl (myfilter x fl l)
This should be called with filterall xs [condition1,condition2...] [[],[]...]

Ordering list of String trough other list of String

I have two lists ["a","b","c","d"] and ["b","d","a","c"]
How can I make a function that orders the first list with the same order of the second one?
In this example something like this:
> ord ["a","b","c","d"] ["b","d","a","c"]
["b","d","a","c"]
all the function I make give me an incomplete list:
ord :: [String] -> [String] -> [String]
ord [] _ = []
ord (h:t) (x:xs) | (h==x) = h:(ord t xs)
| otherwise = ord t (x:xs)
This is only an example; I can't simply present the second list.
Here's a quick and dirty solution that builds the result by grouping each string in the first list by the order in the second (I also renamed ord to orderThese):
orderThese :: [String] -> [String] -> [String]
orderThese _ [] = []
orderThese as (b:bs) = filter (\x -> x == b) as ++ orderThese as bs
As an example, orderThese ["a", "c", "a", "b"] ["b", "a", "c"] returns ["b","a","a","c"].
I think this is what you want:
import Data.Function (on)
import Data.List (elemIndex, sortBy)
ord :: Eq a => [a] -> [a] -> [a]
ord listToSort desiredOrder = sortBy (compare `on` (`elemIndex` desiredOrder)) listToSort
I would suggest that
you give the function a different name, as there is a function called ord in Data.Char; and
you swap the order of the parameters, as it seems more likely that you would want to partially apply the function with a desiredOrder than with a listToSort.