# Find minimum argument with custom comparator

**URL:** <https://discourse.julialang.org/t/find-minimum-argument-with-custom-comparator/47561>\
**Category:** General Usage\
**Tags:** question\
**Created:** [October 1, 2020, 12:54am UTC](https://discourse.julialang.org/t/find-minimum-argument-with-custom-comparator/47561 "2020-10-01T00:54:36Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![timquelch](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/timquelch/32/18278_2.png) [@timquelch](https://discourse.julialang.org/u/timquelch)\
**Post date:** [October 1, 2020, 12:54am UTC](https://discourse.julialang.org/t/find-minimum-argument-with-custom-comparator/47561/1 "2020-10-01T00:54:36Z")

</div>

I feel like something is missing from the available minimum functions. I’ve come across the issue multiple times where i want to get the value of a collection that gives the minimum value of a specified function. i.e. I want to specify my own comparator.

- `min` returns the smallest value in a collection (or the arguments). I assume comparison is done by `isless`. I guess I could overload `isless` but that seems heavy-handed for a one-time minimisation.
- `minimum` also finds the smallest value in a collection. It also provides a method `minimum(f, itr)` which returns the smallest result of calling `f` on `itr`. This is close, but it returns the minimum value of `f`, not the value of `itr` that gives the value of `f`
- `argmin` returns the _index_ of the smallest element, which is helpful, but again doesn’t allow specifying a custom comparator.

So far I’ve been solving this with the below function, but this seems like it might be something that already exists elsewhere.

```
julia> minimumby(f, itr) = itr[argmin(map(f, itr))]
julia> minimumby(x->x^2, [-2, 1, 2, 3, 4])
1

```

Am I missing something? Is this the correct way to do this?

Alternatively, is there a way to specify a temporary method for `isless` that will only be visible within scope?

---

<div class="post-metadata">

**Author:** ![xiaodai](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/xiaodai/32/15937_2.png) [@xiaodai](https://discourse.julialang.org/u/xiaodai)\
**Post date:** [October 1, 2020, 1:39am UTC](https://discourse.julialang.org/t/find-minimum-argument-with-custom-comparator/47561/2 "2020-10-01T01:39:18Z")

</div>

I have seen this raise elsewhere, I suspect a PR to Julia Base would be accepted. Seems like an oversight or lack of time rather than anything else.

For your special-case the more archaic `partialsort` would also work

```julia
x = [-2, 1, 2, 3, 4]

partialsort(x, 1, by = x->x^2)

```

Another interesting way is via

```julia
struct XX
    x
end

Base.isless(x::XX, y::XX) = isless(x.x^2, y.x^2)
Base.isless(x::XX, y) = isless(x.x^2, y)

minimum(XX.(x))

```

But not very practical

---

<div class="post-metadata">

**Author:** ![timquelch](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/timquelch/32/18278_2.png) [@timquelch](https://discourse.julialang.org/u/timquelch)\
**Post date:** [October 1, 2020, 2:28am UTC](https://discourse.julialang.org/t/find-minimum-argument-with-custom-comparator/47561/3 "2020-10-01T02:28:29Z")

</div>

One downside of `partialsort` is that it will copy the whole of `x` which may be expensive.

`sort` (and it’s derivatives) accepts keyword arguments to specify the functions used for ordering, perhaps minimum/maximum should as well.

---

<div class="post-metadata">

**Author:** ![xiaodai](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/xiaodai/32/15937_2.png) [@xiaodai](https://discourse.julialang.org/u/xiaodai)\
**Post date:** [October 1, 2020, 2:55am UTC](https://discourse.julialang.org/t/find-minimum-argument-with-custom-comparator/47561/4 "2020-10-01T02:55:35Z")

</div>

> [@timquelch](#):
>
> One downside of `partialsort` is that it will copy the whole of `x` which may be expensive.

The current `map` approach also copies

---

<div class="post-metadata">

**Author:** ![xiaodai](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/xiaodai/32/15937_2.png) [@xiaodai](https://discourse.julialang.org/u/xiaodai)\
**Post date:** [October 1, 2020, 3:00am UTC](https://discourse.julialang.org/t/find-minimum-argument-with-custom-comparator/47561/5 "2020-10-01T03:00:57Z")

</div>

Found a better approach via mapped arrays

```julia
x = Int.(rand(Int8, 100_000_000))

using BenchmarkTools
using MappedArrays

minimumby_orig(f, itr) = itr[argmin(map(f, itr))]
minimumby(f, x) = x[argmin(mappedarray(f, x))]
f(x) = x^2

@benchmark minimumby_orig(f, x)
@benchmark minimumby(f, x)

```

---

<div class="post-metadata">

**Author:** ![lucas711642](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lucas711642/32/12051_2.png) [@lucas711642](https://discourse.julialang.org/u/lucas711642)\
**Post date:** [October 1, 2020, 3:41am UTC](https://discourse.julialang.org/t/find-minimum-argument-with-custom-comparator/47561/6 "2020-10-01T03:41:23Z")

</div>

> [@timquelch](#):
>
> `argmin(map(f, itr))`

It seems like a missing feature: I think `argmin(f, iter)` and `findmin(f, iter)` should be defined. You can avoid the temporary allocation of `map(f, itr)` using MappedArrays, as suggested by @xiaodai.

You can also implement the function yourself, as it is quite straightforward (remember, your functions in Julia can be as fast as built-in ones).

There are some fun ways to achieve what you want, too:

```julia
reduce([-2, -1, 2, 3, 4]) do x, y
	by = x -> x ^ 2
	by(x) < by(y) ? x : y
end

```

although function `by` is executed more times than needed (which should be avoided if `by` is expensive).

Another way is:

```julia
minimum([-2, -1, 2, 3, 4]) do x
	by = x -> x ^ 2
	by(x), x
end[2]

```

this works because `Tuple`s are compared lexicographically:

```julia
julia> (1, 2) < (2, 1)
true

```

In these examples I’m not considering the case when `f(a) == f(b)` for `a ≠ b`. Also, you may need to adapt these examples if you have special values such as `NaN`.

---

<div class="post-metadata">

**Author:** ![Sukera](https://avatars.discourse-cdn.com/v4/letter/s/ce7236/32.png) [@Sukera](https://discourse.julialang.org/u/Sukera)\
**Post date:** [October 1, 2020, 5:21am UTC](https://discourse.julialang.org/t/find-minimum-argument-with-custom-comparator/47561/7 "2020-10-01T05:21:56Z")

</div>

There’s also `partialsort!`, for in-place sorting.
