Search code examples
ocaml

How to sort a list of tuples using both key and value in OCaml?


I have a list of tuples like (1, "a"), (2, "b"), (3, "c") and I want to sort them like

1 a 2 b 3 b

Where the numerical order takes precedence first and then the alphabetical order. Like if I have

(1, "bob"), (1, "cat")

It would look like

1 bob 1 cat

Here's what I'm trying

let mylist =  List.sort (fun (c1, s1) (c2, s2) -> Pervasives.compare c2 c1 ) -> Pervasives.compare s1 s2) mysortedlist

But this obviously is syntactically incorrect.

Here's the way it is if it only sorted based on numerical order

let mylist =  List.sort (fun (c1, _) (c2, _) -> compare c2 c1) mylist

Also these let statements are part of a much larger block of let and in statements, I'm just focusing on this specific piece.

EDIT: Also I would like to do this in reverse order for the alphabetical order.

For example, (1, "bob"), (1, "cat") (0, "zebra") should look like

0 zebra 1 bob 1 cat


Solution

  • This is pretty straightforward. The function you pass to List.sort just needs to compare the values if the keys are the same.

    List.sort 
      (fun (k1, v1) (k2, v2) -> 
        if k1 = k2 then compare v1 v2 
        else compare k1 k2) 
      [(1, "B"); (2, "C"); (2, "A")]
    

    And the result is:

    [(1, "B"); (2, "A"); (2, "C")]
    

    Alternatively, since it saves us from doing both an equality check on k1 and k2 and possibly running compare on them:

    List.sort 
      (fun (k1, v1) (k2, v2) ->
        match compare k1 k2 with
        | 0 -> compare v1 v2
        | c -> c)
      [(1, "B"); (2, "C"); (2, "A")]
    

    And at the end, we can realize that Stdlib.compare already works this way for tuples and simply write:

    List.sort compare [(1, "B"); (2, "C"); (2, "A")]