Reputation: 377
Let's say we have a custom data type:
data Person = Person { first_name :: String,
last_name :: String,
age :: Int
} deriving (Ord, Eq, Show)
Let's also say I have a list of these Person data types. I've already made a function that sorts these Persons in order, but this is limited to the first value of each person, first_name. What I'm trying to do is modify the Person data type so that this sort function sorts by the age instead of first_name (besides just swapping the value order so that age is first). I know I need to use the instance keyword to write my own compare function for Ord. This is where I'm stuck. Can anyone help me out?
Edit: Yep, this is HW-- I unfortunately need to do it the way I'm describing.
Upvotes: 10
Views: 6517
Reputation: 16
to the addition to above answer adding one more properties to it, that is if a custom data type have any repetition based on some value in that , we can add nub command to remove that. here is the code its working.
import Data.List(sortBy,nub)
import Data.Ord (comparing)
data User = User {
userName::String,
userId::Int
} deriving (Show)
instance Eq User where
(==) u1 u2 = userId u1 Prelude.== userId u2
task1 :: [User] -> [User]
task1 =(sortBy (comparing userId)).nub
the function that can sort in the order of userId as well as remove duplicates from a list of User types [User] userId is a unique property and two user types with the same value for userId are considered duplicates. The function signature is: task1 :: [User] -> [User]
Upvotes: 0
Reputation: 34378
The easiest and most flexible way to deal with that problem, given that there are multiple valid ways to sort Person
values, is not by changing the Ord
instance, but by using a custom sorting function.
import Data.List (sortBy)
import Data.Ord (comparing)
sortByAge :: [Person] -> [Person]
sortByAge = sortBy (comparing age)
sortBy :: (a -> a -> Ordering) -> [a] -> [a]
makes a custom sorting function from a comparison function, while comparing :: Ord a => (b -> a) -> b -> b -> Ordering
makes such a comparison function given, for instance, a field acessor. Be sure to have a good look at the involved type signatures in order to grok how everything fits together.
If you do need to change the Ord
instance, here is how it would go. The syntax is as follows:
instance Ord Person where
compare = undefined -- placeholder
The documentation tells us that, from all of the Ord
methods, we just need to implement compare
. Now, what should we replace undefined
with? We want the comparison to be based on age
, which is an Int
field. Since Int
is an instance of Ord
, the answer is immediate:
instance Ord Person where
compare x y = compare (age x) (age y)
Incidentally, the definition of comparing
is:
comparing :: (Ord a) => (b -> a) -> b -> b -> Ordering
comparing p x y = compare (p x) (p y)
And so we could write the instance in the style we used for the first solution:
instance Ord Person where
compare = comparing age
Upvotes: 21
Reputation: 1519
Remove Ord
from the list of derived type classes, if you have to write your own instance, the compiler generated one will clash. Also, this is not idiomatic, and I don't recommend this in general.
You can write the Ord instance yourself now the usual way:
instance Ord Person where
compare b1 b2 = ...
I'll let you fill in the details as a useful exercise. Once that's done, the normal sort function will do what you want.
Upvotes: 2