How to obtain x instead of f(x) from maximum/minimum(f, itr)

maximum(f, itr) returns f(x). Is there a function that instead returns x, something like sort(itr; by=f)[end]?

I also need the init keyword, which findmax does not have (a similar issue is open for argmax: `argmin`/`argmax` suggest confusing fix in error message for empty collections · Issue #46861 · JuliaLang/julia · GitHub)

There is no such function. There really should be - in fact, the existing maximum(f, itr) is IMO bad API, since it just duplicates maximum(map(f, itr)).

One workaround is to do

prefixed = collect(Iterators.map(i -> (f(i), i)))
last(partialsort!(prefixed, 1; by=first))

But this is not great, since it allocates needlessly.

Although it does not have an init argument, findmax should be the function you are looking for.

In maximum, init is only guaranteed to be used in case of empty input collections. It is thus discouraged to use maximum as follows:

# "find max(5, maximum(xs))"
julia> maximum([1,2,3], init=5)
5

That said, I suggest you decide in your own code on how to treat the empty collection. That is:

if isempty(xs)
  # do something (maybe throw an ArgumentError)
else
  y, i = findmax(f, xs)

  # potentially, if this is what you want (but I am only guessing)
  if y < init
    # do something
  end
end
1 Like

Thank you. I ended up doing something similar to what you proposed.

From a quick scan, there does not seem to be any related issue.

If you don’t want to allocate you can do

first(maximum(x -> (f(x), x), itr))

Isn’t this exactly what argmax does?

argmax(f, domain)

  Return a value x from domain for which f(x) is maximised. If there are multiple maximal values for
  f(x) then the first one will be found.

I need the init keyword.

Drat.

Just to make sure we’re not dealing with the XY problem, could you elaborate on what you use the keyword for?

EDIT: DNF’s right. findmax returns the index of x (and f(x)) whereas argmax returns x.

After taking a second look, I do not need it.
Thank you for asking the question.

Perhaps, pass an init to argmax as follows:

argmax(f, Iterators.rest(itr, init))