简体   繁体   中英

Haskell : How to sort a record type by one attribute if the other is same?

If I just have a very simple custom data type:

data Person = Person {first_name :: String,
                      last_name :: String
                     } deriving (Ord, Eq, Show)

If I have a list of these Person data types, and I want to sort it by last_name if and only if the first_name is the same. But I should not sort the first_name . Simply call the following obviously doesn't work.

import Data.Function (on)
import Data.List (sortBy)

sortBy (compare `on` last_name ) persons

How can I solve it? I do agree it's quite confusing. What I want is that if I have the following (that's definitely not a list of the custom data type, I just want to show it clearly)

[("c","d"),("a","c"),("a","a")]

After sorting I would like to have

[("c","d"),("a","a"),("a","c")]

instead of

 [("a","a"),("a","c"),("c","d")]

So the idea is that first element should still appear at first, since it does not have the same first_name with the other two. Is that possible? Thanks!

The comparison function you want is to treat two Person values as equal if their first names are different. If the first names are the same, then compare based on the last name.

Since sort in Haskell is stable, if an element a precedes an equal element b in the input, it will also precede b in the output. In this case, two elements with different first names will compare as equal, so sort will really only sort the consecutive subsequences sharing a first name.

There is probably a clever way to implement this, but a straightforward comparison function might look like

foo :: Person -> Person -> Ordering
foo x y | first_name x /= first_name y = EQ
        | otherwise                   = (comparing last_name) x y

Given people = [ Person "A" "B",Person "Z" "C",Person "Z" "A"] , you can see the difference between sortBy (comparing last_name) and sortBy foo .

> people
[Person {first_name = "A", last_name = "B"},Person {first_name = "Z", last_name = "C"},Person {first_name = "Z", last_name = "A"}]
> sortBy (comparing last_name) people
[Person {first_name = "Z", last_name = "A"},Person {first_name = "A", last_name = "B"},Person {first_name = "Z", last_name = "C"}]
> sortBy foo people
[Person {first_name = "A", last_name = "B"},Person {first_name = "Z", last_name = "A"},Person {first_name = "Z", last_name = "C"}]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM