Reputation: 715
How can I sort animals by their name? Names are represented by String
values.
data Animal = Cat String | Dog String | Fox String deriving (Show)
testAnimals = [(Dog "c"),(Fox "a"),(Cat "b")]
sortAnimalsByName :: [Animal] -> [Animal]
sortAnimalsByName animals = undefined
The query:
sortAnimalsByName testAnimals
should return:
[(Fox "a"),(Cat "b"),(Dog "c")]
I think the function sort :: Ord a => [a] -> [a]
from Data.List
should be used, but how?
Upvotes: 2
Views: 115
Reputation: 228
Joining answers, given by @Willem Van Onsem (record-syntax) and @urbanslug (implementing Ord
class instance) we obtain the simplest one:
data Animal a = Cat {name :: a}
| Dog {name :: a}
| Fox {name :: a} deriving (Show, Eq)
instance Ord a => Ord (Animal a) where
a1 `compare` a2 = name a1 `compare` name a2
So that it is possible to say simply
λ> sort [(Dog "c"),(Fox "a"),(Cat "b")]
[Fox {name = "a"},Cat {name = "b"},Dog {name = "c"}]
Using Data.Ord.comparing
function one coud write even more precisely:
instance Ord a => Ord (Animal a) where compare = comparing name
Upvotes: 0
Reputation: 476567
First you better define an animalName
function:
animalName :: Animal -> String
animalName (Cat n) = n
animalName (Dog n) = n
animalName (Fox n) = n
Simply use sortBy
where the comparator is simply compare
but on
the animalName
:
import Data.Function(on)
import Data.List(sortBy)
sortAnimalsByName :: [Animal] -> [Animal]
sortAnimalsByName = sortBy (compare `on` animalName)
Or you can - like @JonPurdy suggests, use comparing
which is basically the same as on compare
:
import Data.Ord(comparing)
sortAnimalsByName :: [Animal] -> [Animal]
sortAnimalsByName = sortBy (comparing animalName)
or even shorter:
import Data.List(sortOn)
sortAnimalsByName :: [Animal] -> [Animal]
sortAnimalsByName = sortOn animalName
You can furthermore omit the definition of animalName
if you use record-syntax:
--omitting the `animalName` function
data Animal = Cat { animalName :: String} |
Dog { animalName :: String} |
Fox { animalName :: String} deriving (Show)
Upvotes: 8
Reputation: 146
Edited Thank you @WillemVanOnsem for your comment.
First of all this should be a proper sum type so that you can avoid repeating String all over. Meaning, you should make it
type Animal a = Dog a | Cat a | Fox a
This way you would have a more general Animal type (which is a good thing). One of the advantages of a more general type would be it would allow you to create more precise typeclass instances of Animal.
However in your case the easiest way would be to derive Show
, Eq
, and then write an Ord instance like so:
data Animal = Cat String | Dog String | Fox String deriving (Show, Eq)
instance Ord Animal where
(Cat x) `compare` (Cat y) = x `compare` y
(Cat x) `compare` (Dog y) = x `compare` y
(Cat x) `compare` (Fox y) = x `compare` y
(Dog x) `compare` (Cat y) = x `compare` y
(Dog x) `compare` (Dog y) = x `compare` y
(Dog x) `compare` (Fox y) = x `compare` y
(Fox x) `compare` (Cat y) = x `compare` y
(Fox x) `compare` (Dog y) = x `compare` y
(Fox x) `compare` (Fox y) = x `compare` y
or
data Animal a = Cat a | Dog a | Fox a deriving (Show, Eq)
instance Ord a => Ord (Animal a) where
(Cat x) `compare` (Cat y) = x `compare` y
(Cat x) `compare` (Dog y) = x `compare` y
(Cat x) `compare` (Fox y) = x `compare` y
(Dog x) `compare` (Cat y) = x `compare` y
(Dog x) `compare` (Dog y) = x `compare` y
(Dog x) `compare` (Fox y) = x `compare` y
(Fox x) `compare` (Cat y) = x `compare` y
(Fox x) `compare` (Dog y) = x `compare` y
(Fox x) `compare` (Fox y) = x `compare` y
You can use sort
because now Animal
has an Ord
instance
-- sort :: Ord a => [a] -> [a]
sort testAnimals
Point is: use typeclasses. That is just the easiest way for your simple type.
Upvotes: 3