Search code examples
arrayssortingtuplesjuliareverse

Sort array of tuple over multiple indices, only one reversed in Julia


I have an Array{Tuple{Float64, Int}}, I want to sort it in reversed order according to the first entries and in straight order according to the second one.

Example: if a=[(1.0,2),(3.0,1),(2.0,3),(2.0,2)], after calling sort!(a, ...), I'd like to get [(3.0,1),(2.0,2),(2.0,3),(1.0,2)] where the first dimension is sorted from greater values to smaller ones and, when two entries are equal, the second dimension is sorted from small to big.


Solution

  • There are a couple ways to customize the sorting order, using either the lt or by keyword arguments to sort (or sort!):

    1. using lt: you define a custom "less-than" operator for tuples. You encode in this comparison operator more or less the order as you'd express it in a plain way:
    julia> a=[(1.0,2),(3.0,1),(2.0,3),(2.0,2)];
    
    julia> mylt((x1,y1), (x2,y2)) = x1>x2 || (x1==x2 && y1<y2)
    mylt (generic function with 1 method)
    
    julia> sort(a, lt=mylt)
    4-element Array{Tuple{Float64,Int64},1}:
     (3.0, 1)
     (2.0, 2)
     (2.0, 3)
     (1.0, 2)
    

    2.using by: you define a transformation that is applied to each element in the array, and by which elements are sorted:

    julia> x_desc_then_y_asc((x,y)) = (-x, y)
    x_desc_then_y_asc (generic function with 1 method)
    
    julia> sort(a, by=x_desc_then_y_asc)
    4-element Array{Tuple{Float64,Int64},1}:
     (3.0, 1)
     (2.0, 2)
     (2.0, 3)
     (1.0, 2)
    

    The helper function in that variant is so short that I would make an anonymous function out of it:

    julia> sort(a, by=((x,y),)->(-x,y))
    4-element Array{Tuple{Float64,Int64},1}:
     (3.0, 1)
     (2.0, 2)
     (2.0, 3)
     (1.0, 2)
    

    PS: note how "tuple destructuring" is used in all these examples in order to avoid indexing explicitly into tuples.