# Minmax with `lt` keyword argument?

Is there a “minmax” that can have a `lt` keyword like what `sort` does? For example, some method that does the following:

``````minmax(x, y; lt=isless) = lt(x,y) ? (x,y) : (y,x)
``````

Can you give a small example in running code, or if not possible, an example with small input and the expected output.

I’ve updated the op

`min, max = sort([x, y], lt=...)`

I don’t think there is function like that. It seems strange to ask for it though. For example, if you wish to switch the order (`lt=(x, y) -> x ≥ y`), why not simply do `max, min = minmax(x, y)`?

1 Like
``````ifelse(lt(x,y), (x,y), (y,x))
``````

is very simple and fast.

I would not call this `minmax`, it has different semantics.

But defining and using any function in Julia like this is totally fine.
But note, the returning type is a Tuple which type depends on the arguments. So this function is not type stable since the return type would depend on the value of the numbers.

``````julia> function my_function(x, y; lt=isless)
return lt(x,y) ? (x,y) : (y,x)
end
my_function (generic function with 2 methods)

julia> @code_warntype my_function(1, 1.0)
MethodInstance for my_function(::Int64, ::Float64)
from my_function(x, y; lt) @ Main REPL[12]:1
Arguments
#self#::Core.Const(my_function)
x::Int64
y::Float64
Body::Union{Tuple{Float64, Int64}, Tuple{Int64, Float64}}
1 ─ %1 = Main.:(var"#my_function#3")(Main.isless, #self#, x, y)::Union{Tuple{Float64, Int64}, Tuple{Int64, Float64}}
└──      return %1
``````

More safe would be to restrict the function arguments to have the same type, or do type promotion to the same type.

``````julia> function my_function2(x::T, y::T; lt=isless) where T
return lt(x,y) ? (x,y) : (y,x)
end
my_function2 (generic function with 1 method)

julia> my_function2(1, 1.0)
ERROR: MethodError: no method matching my_function2(::Int64, ::Float64)

Closest candidates are:
my_function2(::T, ::T; lt) where T
@ Main REPL[16]:1

Stacktrace:
[1] top-level scope
@ REPL[17]:1

julia> my_function2(1, 1)
(1, 1)

julia> @code_warntype my_function2(1, 1)
MethodInstance for my_function2(::Int64, ::Int64)
from my_function2(x::T, y::T; lt) where T @ Main REPL[16]:1
Static Parameters
T = Int64
Arguments
#self#::Core.Const(my_function2)
x::Int64
y::Int64
Body::Tuple{Int64, Int64}
1 ─ %1 = Main.:(var"#my_function2#5")(Main.isless, #self#, x, y)::Tuple{Int64, Int64}
└──      return %1

``````
1 Like

It seems like this is maybe looking for something a tad more generic like “sort a short tuple.”

I found this snippet I had lying around. The syntax here is a tad unusual due to outsourcing the ordering to my `makeorder` function, but something like the following might fit your bill (I leave it as an exercise to the reader to write the length=2 version). You can refactor the `makeorder` component to expose the sorting keywords directly within `sorttup`.

``````makeorder(;lt=isless,by=identity,rev::Union{Bool,Nothing}=nothing,order::Base.Order.Ordering=Base.Order.Forward,) = Base.Order.ord(lt,by,rev,order)

function sorttup((a,b,c)::NTuple{3},ordr=makeorder())
# sort 3 values
a,b = ifelse(Base.lt(ordr,b,a),(b,a),(a,b))
b,c = ifelse(Base.lt(ordr,c,b),(c,b),(b,c))
a,b = ifelse(Base.lt(ordr,b,a),(b,a),(a,b))
return a,b,c
end

function sorttup((a,b,c,d)::NTuple{4},ordr=makeorder())
# sort 4 values
a,b = ifelse(Base.lt(ordr,b,a),(b,a),(a,b))
c,d = ifelse(Base.lt(ordr,d,c),(d,c),(c,d))
a,c = ifelse(Base.lt(ordr,c,a),(c,a),(a,c))
b,d = ifelse(Base.lt(ordr,d,b),(d,b),(b,d))
b,c = ifelse(Base.lt(ordr,c,b),(c,b),(b,c))
return a,b,c,d
end
``````
1 Like

There is also TupleTools.jl and this sort: Home · TupleTools.jl

2 Likes

TupleTools.jl is slower than SortingNetworks.jl but the latter doesn’t support `lt` or `by`. It’s exactly the motivation of this post though.

You apply this function to some big data?
Can you share an example for it?

Not too big, but kind of low-level. I need to sort intervals before calculating the union of them.

Other answers are good; I just want to point out that you may also be interested in

``````extrema(f, (x,y))
``````

which always uses `<` for comparisons, but allows you to preprocess the input with the function `f`. (Plus it extends nicely to longer collections.) In the trivial case, it compiles down to exactly the same code as other solutions.

Also note that most answers use the tuple `(x, y)` instead of the vector `[x, y]` because the vector would allocate, whereas the tuple will not.

3 Likes