# Obtaining the last element of a ProductIterator

**URL:** <https://discourse.julialang.org/t/obtaining-the-last-element-of-a-productiterator/54259>\
**Category:** General Usage\
**Tags:** question, iterators\
**Created:** [January 30, 2021, 9:15am UTC](https://discourse.julialang.org/t/obtaining-the-last-element-of-a-productiterator/54259 "2021-01-30T09:15:45Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![jishnub](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jishnub/32/33620_2.png) [@jishnub](https://discourse.julialang.org/u/jishnub)\
**Post date:** [January 30, 2021, 9:15am UTC](https://discourse.julialang.org/t/obtaining-the-last-element-of-a-productiterator/54259/1 "2021-01-30T09:15:45Z")

</div>

Currently there is no way to obtain the last element of an iterator product, for example:

```julia
julia> it = Iterators.product(1:3, 2:3)
Base.Iterators.ProductIterator{Tuple{UnitRange{Int64}, UnitRange{Int64}}}((1:3, 2:3))

julia> last(it)
ERROR: MethodError: no method matching lastindex(::Base.Iterators.ProductIterator{Tuple{UnitRange{Int64}, UnitRange{Int64}}})

```

However should it not be possible to define this by collating the last elements of the component iterators?

```julia
julia> Base.last(it::Iterators.ProductIterator) = map(last, it.iterators)

julia> last(it)
(3, 3)

```

---

<div class="post-metadata">

**Author:** ![sijo](https://avatars.discourse-cdn.com/v4/letter/s/da6949/32.png) [@sijo](https://discourse.julialang.org/u/sijo)\
**Post date:** [January 30, 2021, 11:59am UTC](https://discourse.julialang.org/t/obtaining-the-last-element-of-a-productiterator/54259/2 "2021-01-30T11:59:02Z")

</div>

In a future version (1.6?) the following works:

```julia
julia> it = Iterators.product(1:3, 2:3)
Base.Iterators.ProductIterator{Tuple{UnitRange{Int64}, UnitRange{Int64}}}((1:3, 2:3))

julia> last(it, 1)
1-element Vector{Tuple{Int64, Int64}}:
 (3, 3)

```

This was added in [https://github.com/JuliaLang/julia/pull/34868](https://github.com/JuliaLang/julia/pull/34868)

This works as long as the iterator implements `reverse`. There was recently a discussion here about the difficulties of adding a generic `last` method: [Getting the last element of an iterator](https://discourse.julialang.org/t/getting-the-last-element-of-an-iterator/49696/). I planned to submit a PR but as explained in that thread I couldn’t find an implementation that was performant and backward-compatible.

As a workaround @mschauer suggested `foldl((_, y) -> y, itr)`, which also works fine here:

```julia
julia> foldl((_,y)->y, Iterators.product(1:3, 2:3))
(3, 3)

```

---

<div class="post-metadata">

**Author:** ![jishnub](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jishnub/32/33620_2.png) [@jishnub](https://discourse.julialang.org/u/jishnub)\
**Post date:** [January 31, 2021, 7:43am UTC](https://discourse.julialang.org/t/obtaining-the-last-element-of-a-productiterator/54259/3 "2021-01-31T07:43:11Z")

</div>

I realize that a general `O(1)` `last` method is not possible in most cases, and the `foldl` equivalents would be `O(n)` if I understand correctly. In this specific case of a `ProductIterator`, it feels like it should support fetching the last element in `O(1)` time, unless there are some corner cases that I am missing out on?

The method `last(it, 1)` seems to rely on actually collecting the elements of the iterator, which may be avoided in many cases?

---

<div class="post-metadata">

**Author:** ![sijo](https://avatars.discourse-cdn.com/v4/letter/s/da6949/32.png) [@sijo](https://discourse.julialang.org/u/sijo)\
**Post date:** [January 31, 2021, 10:16am UTC](https://discourse.julialang.org/t/obtaining-the-last-element-of-a-productiterator/54259/4 "2021-01-31T10:16:09Z")

</div>

It is O(1) in this case. According to `@less Iterators.reverse(it)` the implementation is:

```julia
last(itr, n::Integer) = reverse!(collect(Iterators.take(Iterators.reverse(itr), n)))

```

The inner `reverse(itr)` will call `reverse` on each iterator in the product, for example `reverse(1:3)` which returns 3:-1:1 without any collection.

`collect` operates on the `take` iterator so it only collects 1 element.
