# Findmin/max and generators

**URL:** <https://discourse.julialang.org/t/findmin-max-and-generators/88473>\
**Category:** General Usage\
**Created:** [October 9, 2022, 10:33am UTC](https://discourse.julialang.org/t/findmin-max-and-generators/88473 "2022-10-09T10:33:43Z")\
**Posts on this page:** 14\
**Page:** 1

<div class="post-metadata">

**Author:** ![goerch](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/goerch/32/29122_2.png) [@goerch](https://discourse.julialang.org/u/goerch)\
**Post date:** [October 9, 2022, 10:33am UTC](https://discourse.julialang.org/t/findmin-max-and-generators/88473/1 "2022-10-09T10:33:43Z")

</div>

I noticed the following oddity

```julia
println(findmin(identity, x for x in [3, 2, 1]))
println(findmin(identity, x for x in [3, 2, 1] if x != 1))

```

showing

```julia
(1, 3)
ERROR: MethodError: no method matching keys(::Base.Iterators.Filter{var"#9#10", Vector{Int64}})

```

Do you know an easy fix for this?

---

<div class="post-metadata">

**Author:** ![Dan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dan/32/42581_2.png) [@Dan](https://discourse.julialang.org/u/Dan)\
**Post date:** [October 9, 2022, 11:20am UTC](https://discourse.julialang.org/t/findmin-max-and-generators/88473/2 "2022-10-09T11:20:36Z")

</div>

This seems to fix the issue:

```julia
julia> import Base: keys

julia> keys(itr::Base.Iterators.Filter) = Iterators.map(first, enumerate(itr))

julia> println(findmin(identity, x for x in [3, 2, 1] if x != 1))
(2, 2)

```

Might be a good idea to make an appropriate change in Base. Not sure this fix is ideal (as iterator might be cycled twice during `findmin` which is bad for heavy iterations)

---

<div class="post-metadata">

**Author:** ![Dan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dan/32/42581_2.png) [@Dan](https://discourse.julialang.org/u/Dan)\
**Post date:** [October 9, 2022, 11:35am UTC](https://discourse.julialang.org/t/findmin-max-and-generators/88473/3 "2022-10-09T11:35:30Z")

</div>

This update, moves the fix up the call chain and prevents the double cycling of iterator. On the other hand, I’m not familiar enough with other side-effects this fix might have.

```julia
julia> import Base: pairs

julia> pairs(g::Base.Generator{T,I}) where {T<:Base.Iterators.Filter, I} =
         Iterators.map(((i,v),) -> i=>v, enumerate(g.iter))
pairs (generic function with 11 methods)

julia> findmin(x for x in [3,2,1] if x != 1)
(2, 2)

```

---

<div class="post-metadata">

**Author:** ![JeffreySarnoff](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jeffreysarnoff/32/1980_2.png) [@JeffreySarnoff](https://discourse.julialang.org/u/JeffreySarnoff)\
**Post date:** [October 9, 2022, 11:28pm UTC](https://discourse.julialang.org/t/findmin-max-and-generators/88473/4 "2022-10-09T23:28:26Z")

</div>

adding parens works

```julia
julia> findmin(identity, (x for x in [3, 2, 1] if x != 1))
(2, 2)

```

---

<div class="post-metadata">

**Author:** ![goerch](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/goerch/32/29122_2.png) [@goerch](https://discourse.julialang.org/u/goerch)\
**Post date:** [October 11, 2022, 2:19am UTC](https://discourse.julialang.org/t/findmin-max-and-generators/88473/5 "2022-10-11T02:19:40Z")

</div>

@Dan: to make this work for

```julia
@btime findmin(x * x for x in (3, 2, 1) if x != 1)

```

I had to use

```julia
Base.pairs(g::Base.Generator{I,F}) where {I<:Base.Iterators.Filter, F<: Function} = Iterators.map(=>, 1:typemax(Int), g)

```

To all: thanks for your help!

---

<div class="post-metadata">

**Author:** ![uniment](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/uniment/32/24532_2.png) [@uniment](https://discourse.julialang.org/u/uniment)\
**Post date:** [October 11, 2022, 2:44am UTC](https://discourse.julialang.org/t/findmin-max-and-generators/88473/6 "2022-10-11T02:44:42Z")

</div>

You shouldn’t be using `findmin` here, but instead `minimum`.

`findmin` returns a tuple of the minimum value _and_ its index in the collection or iterator. If you pass to it a filtered iterator, the index is meaningless, so it rightly throws a fit. Thus, to get it to work, you’re forced to collect all the iterator’s values into memory as a tuple or array first before calling `findmin` on it.

`minimum` simply returns the minimum value without concern for what its index in the collection or iterator is, so you can freely pass filtered iterators to it.

```julia
julia> minimum(x^2 for x ∈ (3, 2, 1) if x ≠ 1)
4

```

---

<div class="post-metadata">

**Author:** ![Dan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dan/32/42581_2.png) [@Dan](https://discourse.julialang.org/u/Dan)\
**Post date:** [October 11, 2022, 3:22am UTC](https://discourse.julialang.org/t/findmin-max-and-generators/88473/7 "2022-10-11T03:22:21Z")

</div>

@uniment is right. It is important to get the semantics accurately.  
Perhaps what we are looking for is:

```julia
julia> minimum(x => x * x for x in (6,5,4) if x != 4)
5 => 25

```

The confusing bit is that above expression calculates the _mathematical_ `argmin` of the expression `x^2` in the set. The Julia `argmin` function calculates the minimum and its index, which is the _mathematical_ argmin of function `i -> val[i]`

---

<div class="post-metadata">

**Author:** ![uniment](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/uniment/32/24532_2.png) [@uniment](https://discourse.julialang.org/u/uniment)\
**Post date:** [October 11, 2022, 3:36am UTC](https://discourse.julialang.org/t/findmin-max-and-generators/88473/8 "2022-10-11T03:36:49Z")

</div>

> [@Dan](#):
>
> Perhaps what we are looking for is:
> 
> ```julia
> julia> minimum(x => x * x for x in (6,5,4) if x != 4)
> 5 => 25
> 
> ```
> 
> The confusing bit is that above expression calculates the _mathematical_ `argmin` of the expression `x^2` in the set. The Julia `argmin` function calculates the minimum and its index, which is the _mathematical_ argmin of function `i -> val[i]`

This is incorrect; this code is not calculating the _argmin_, but instead the _minimum argument_.

---

<div class="post-metadata">

**Author:** ![uniment](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/uniment/32/24532_2.png) [@uniment](https://discourse.julialang.org/u/uniment)\
**Post date:** [October 11, 2022, 4:01am UTC](https://discourse.julialang.org/t/findmin-max-and-generators/88473/10 "2022-10-11T04:01:15Z")

</div>

If you’re using `findmin` because you’re hoping to find both the minimum squared value and its index, for any value not equal to 1, then this is likely what you’re looking for:

```julia
julia> findmin(x==1 ? Inf : x^2 for x ∈ (4, 3, 2, 1))
(4, 3)

```

---

<div class="post-metadata">

**Author:** ![Dan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dan/32/42581_2.png) [@Dan](https://discourse.julialang.org/u/Dan)\
**Post date:** [October 11, 2022, 9:16am UTC](https://discourse.julialang.org/t/findmin-max-and-generators/88473/11 "2022-10-11T09:16:49Z")

</div>

Again @uniment , you are right. A `Pair` is ordered by key first and value later. Somehow I had assumed it was ordered by value first. To get the result I wanted:

```julia
julia> minimum((x * x, x) for x in (6,5,4) if x != 4)
(25, 5)

```

which is the same output, but gets things right when function is not order preserving.

---

<div class="post-metadata">

**Author:** ![Dan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dan/32/42581_2.png) [@Dan](https://discourse.julialang.org/u/Dan)\
**Post date:** [October 11, 2022, 9:19am UTC](https://discourse.julialang.org/t/findmin-max-and-generators/88473/12 "2022-10-11T09:19:01Z")

</div>

> ```julia
> julia> findmin(x==1 ? Inf : x^2 for x ∈ (4, 3, 2, 1))
> (4, 3)
> 
> ```

This is:  
a. not type stable.  
b. the second tuple number is somewhat useless, you would want the value of `x` for an `argmin` of the function x^2.

---

<div class="post-metadata">

**Author:** ![aplavin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aplavin/32/222056_2.png) [@aplavin](https://discourse.julialang.org/u/aplavin)\
**Post date:** [October 11, 2022, 10:45am UTC](https://discourse.julialang.org/t/findmin-max-and-generators/88473/13 "2022-10-11T10:45:15Z")

</div>

> [@Dan](#):
>
> a. not type stable.

Not type stable just because the generator iterates both ints and floats. Replace `x^2` by `x^2.0` and it becomes stable.

---

<div class="post-metadata">

**Author:** ![lmiq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lmiq/32/18314_2.png) [@lmiq](https://discourse.julialang.org/u/lmiq)\
**Post date:** [October 11, 2022, 11:13am UTC](https://discourse.julialang.org/t/findmin-max-and-generators/88473/14 "2022-10-11T11:13:48Z")

</div>

> [@Dan](#):
>
> `findmin(x==1 ? Inf : x^2 for x ∈ (4, 3, 2, 1))`

```julia
julia> findmin(x==1 ? typemax(x) : x^2 for x ∈ (4, 3, 2, 1))
(4, 3)

```

(`x^2.0` is much slower than `x^2`).

---

<div class="post-metadata">

**Author:** ![lmiq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lmiq/32/18314_2.png) [@lmiq](https://discourse.julialang.org/u/lmiq)\
**Post date:** [October 11, 2022, 11:18am UTC](https://discourse.julialang.org/t/findmin-max-and-generators/88473/15 "2022-10-11T11:18:52Z")

</div>

Not here:

```julia
julia> findmin(identity, (x for x in [3, 2, 1] if x != 1))
ERROR: MethodError: no method matching keys(::Base.Iterators.Filter{var"#23#24", Vector{Int64}})

```

Anyway, I think the original question is well worth an issue. I don’t see why it _shouldn’t_ work. ([did it here](https://github.com/JuliaLang/julia/issues/47124))
