# Find positions of minimum and maximum values at once

**URL:** <https://discourse.julialang.org/t/find-positions-of-minimum-and-maximum-values-at-once/120908>\
**Category:** General Usage\
**Created:** [October 4, 2024, 11:30am UTC](https://discourse.julialang.org/t/find-positions-of-minimum-and-maximum-values-at-once/120908 "2024-10-04T11:30:40Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![heliosdrm](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/heliosdrm/32/3851_2.png) [@heliosdrm](https://discourse.julialang.org/u/heliosdrm)\
**Post date:** [October 4, 2024, 11:30am UTC](https://discourse.julialang.org/t/find-positions-of-minimum-and-maximum-values-at-once/120908/1 "2024-10-04T11:30:40Z")

</div>

The minimum and maximum values of an iterable can be found either with `minimum` and `maximum`, and more conveniently(\*) with `extrema`.

Also, the positions of those extrema can be found with `findmin` and `findmax` (or `argmin`, `argmax`) separately. But I don’t know of any function in Base to obtain both at once. Did I miss it, or is there any reason why `findextrema` or `argextrema` are not desirable?

(\*) I also expected that `extrema` would be more performant than `minimum` and `maximum` separately, [but it isn’t](https://github.com/JuliaLang/julia/issues/31442).

---

<div class="post-metadata">

**Author:** ![DNF](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnf/32/10191_2.png) [@DNF](https://discourse.julialang.org/u/DNF)\
**Post date:** [October 4, 2024, 5:30pm UTC](https://discourse.julialang.org/t/find-positions-of-minimum-and-maximum-values-at-once/120908/2 "2024-10-04T17:30:20Z")

</div>

> [@heliosdrm](#):
>
> I also expected that `extrema` would be more performant than `minimum` and `maximum` separately, [but it isn’t](https://github.com/JuliaLang/julia/issues/31442)

Are you sure about that?

```julia
julia> @btime (minimum(x), maximum(x)) setup=(x=randn(10_000))
  62.222 μs (0 allocations: 0 bytes)
(-3.6004358584957203, 4.050586645172776)

julia> @btime extrema(x) setup=(x=randn(10_000))
  30.402 μs (0 allocations: 0 bytes)
(-4.16019176790883, 3.907089251771992)

```

---

<div class="post-metadata">

**Author:** ![DNF](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnf/32/10191_2.png) [@DNF](https://discourse.julialang.org/u/DNF)\
**Post date:** [October 4, 2024, 5:34pm UTC](https://discourse.julialang.org/t/find-positions-of-minimum-and-maximum-values-at-once/120908/3 "2024-10-04T17:34:05Z")

</div>

Surprisingly, though, `findmin/max` is faster than `minimum/maximum`:

```julia
julia> @btime (findmin(x), findmax(x)) setup=(x=randn(10_000))
  33.522 μs (0 allocations: 0 bytes)
((-3.451315161397838, 4075), (3.534236845843399, 8767))

```

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [October 4, 2024, 5:34pm UTC](https://discourse.julialang.org/t/find-positions-of-minimum-and-maximum-values-at-once/120908/4 "2024-10-04T17:34:47Z")

</div>

> [@DNF](#):
>
> > [@heliosdrm](#):
> >
> > I also expected that `extrema` would be more performant than `minimum` and `maximum` separately, [but it isn’t](https://github.com/JuliaLang/julia/issues/31442)
> 
> Are you sure about that?

See the linked issue — `extrema` fails to SIMD-vectorize on some systems, apparently. So it depends on your CPU.

---

<div class="post-metadata">

**Author:** ![mbauman](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mbauman/32/31082_2.png) [@mbauman](https://discourse.julialang.org/u/mbauman)\
**Post date:** [October 4, 2024, 5:40pm UTC](https://discourse.julialang.org/t/find-positions-of-minimum-and-maximum-values-at-once/120908/5 "2024-10-04T17:40:19Z")

</div>

Yep, exactly. I still see that issue on my Mac’s ARM processor. The 4x difference is quite telling.

```julia-repl
julia> @btime (minimum(x), maximum(x)) setup=(x=randn(10_000))
  12.416 μs (0 allocations: 0 bytes)
(-3.989876350573738, 3.444422298891971)

julia> @btime (extrema(x)) setup=(x=randn(10_000))
  49.042 μs (0 allocations: 0 bytes)
(-3.639776550439404, 3.7251211876149286)

julia> @btime (minimum(x), maximum(x)) setup=(x=rand(-100:100, 10_000))
  6.125 μs (0 allocations: 0 bytes)
(-100, 100)

julia> @btime (extrema(x)) setup=(x=rand(-100:100, 10_000))
  3.073 μs (0 allocations: 0 bytes)
(-100, 100)

```

---

<div class="post-metadata">

**Author:** ![DNF](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnf/32/10191_2.png) [@DNF](https://discourse.julialang.org/u/DNF)\
**Post date:** [October 4, 2024, 5:48pm UTC](https://discourse.julialang.org/t/find-positions-of-minimum-and-maximum-values-at-once/120908/6 "2024-10-04T17:48:35Z")

</div>

Here’s a quick-and-dirty implementation of `findextrema` which is much faster on my laptop at least:

```julia
function findextrema(x)
    isempty(x) && throw(ArgumentError("reducing over an empty collection is not allowed."))
    minind = maxind = firstindex(x)
    minval = maxval = first(x)
    for (i, val) in pairs(x)
        (minind, minval) = ifelse(val < minval, (i, val), (minind, minval))
        (maxind, maxval) = ifelse(val > maxval, (i, val), (maxind, maxval))
    end
    return (minind, minval), (maxind, maxval)
end

```

Benchmark:

```julia
julia> @btime extrema(x) setup=(x=randn(10_000))
  30.468 μs (0 allocations: 0 bytes)
(-4.460544193815779, 3.614705321217675)

julia> @btime (findmin(x), findmax(x)) setup=(x=randn(10_000))
  33.541 μs (0 allocations: 0 bytes)
((-3.9324850320327984, 4062), (3.7604910904523896, 502))

julia> @btime findextrema(x) setup=(x=randn(10_000))
  8.370 μs (0 allocations: 0 bytes)
((9567, -3.7857378107436417), (4247, 3.8199475029280956))

```

This is an Intel processor, though.
