# Nested iterators

**URL:** <https://discourse.julialang.org/t/nested-iterators/52180>\
**Category:** General Usage\
**Created:** [December 21, 2020, 3:08pm UTC](https://discourse.julialang.org/t/nested-iterators/52180 "2020-12-21T15:08:42Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![phendric](https://avatars.discourse-cdn.com/v4/letter/p/58f4c7/32.png) [@phendric](https://discourse.julialang.org/u/phendric)\
**Post date:** [December 21, 2020, 3:08pm UTC](https://discourse.julialang.org/t/nested-iterators/52180/1 "2020-12-21T15:08:42Z")

</div>

I want to nest a couple of Iterators calls, but am not getting the result I expect.

I want to iterate through the cartesian product of a repeated array, except I don’t know ahead of time how many times the array will be repeated. Here’s a hard-coded example of 3 repetitions:

```julia
julia> for ii in Iterators.product([3,7], [3,7], [3,7]); println(ii); end
(3, 3, 3)
(7, 3, 3)
(3, 7, 3)
(7, 7, 3)
(3, 3, 7)
(7, 3, 7)
(3, 7, 7)
(7, 7, 7)

```

In Python, this can be done using the “repeat” keyword:

```julia
In [1]: import itertools as it
In [2]: for ii in it.product([3,7], repeat=3): print(ii)
(3, 3, 3)
(3, 3, 7)
(3, 7, 3)
(3, 7, 7)
(7, 3, 3)
(7, 3, 7)
(7, 7, 3)
(7, 7, 7)

```

It looks like “repeat” isn’t a keyword in Iterators.product, so I thought I could do the same thing with a nested Iterators.repeated call:

```julia
julia> for ii in Iterators.repeated([3,7], 3); println(ii); end
[3, 7]
[3, 7]
[3, 7]

```

```julia
julia> for ii in Iterators.product(Iterators.repeated([3,7], 3)); println(ii); end
([3, 7],)
([3, 7],)
([3, 7],)

```

That obviously doesn’t do what I expect or want.

Questions:

1. Why does it do what it does instead of what I expect?
2. How can I accomplish what I want?

PS - this is Julia 1.5.1

---

<div class="post-metadata">

**Author:** ![pixel27](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pixel27/32/8902_2.png) [@pixel27](https://discourse.julialang.org/u/pixel27)\
**Post date:** [December 21, 2020, 3:24pm UTC](https://discourse.julialang.org/t/nested-iterators/52180/2 "2020-12-21T15:24:13Z")

</div>

No time to answer 1, but for 2 you could do something like:

```julia
a=[[ 3, 7], [3, 7], [3, 7] ]
for ii in Iterators.product(a...); println(ii); end

```

---

<div class="post-metadata">

**Author:** ![cgeoga](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cgeoga/32/216186_2.png) [@cgeoga](https://discourse.julialang.org/u/cgeoga)\
**Post date:** [December 21, 2020, 3:34pm UTC](https://discourse.julialang.org/t/nested-iterators/52180/3 "2020-12-21T15:34:19Z")

</div>

It looks like `Iterators.repeated([3,7], 3)` gives you a single-index iterable of length 3, whose elements are `[3,7]`. You can confirm that by calling `collect` on it. So your `product` call is the same as doing `Iterators.product([a,a,a])`, which is just one iterable. That’s why that is doing what it’s doing.

Another suggestion to get what you want would be to use `fill` and splatting. Probably the best analog to your python syntax would be `for ii in Iterators.product(fill([3,7], 3)...) ; println(ii) ; end`. The splatting syntax is important here, because that’s exactly what changes the arguments given to `product` from a single iterator (so a trivial product) to three iterators, which is what you want.

As a disclaimer, that is probably not the fastest or most efficient way to implement that thing. But hopefully it helps to understand what’s going on.

---

<div class="post-metadata">

**Author:** ![pixel27](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pixel27/32/8902_2.png) [@pixel27](https://discourse.julialang.org/u/pixel27)\
**Post date:** [December 21, 2020, 3:51pm UTC](https://discourse.julialang.org/t/nested-iterators/52180/4 "2020-12-21T15:51:16Z")

</div>

Like @cgeoga said `Iterators.repeated()` gives you an iterator that repeats the value N times. But `Iterators.product()` wants each iterator as it’s own argument, which is basically what the splat (…) is doing. You could do:

```julia
Iterators.product(collect(Iterators.repeated([3, 7], 3))...)

```

But that’s probably getting silly, it creates a iterator to give you the array 3 times, collects all 3 instances into an array then splats it into product. @cgeoga’s `fill` example is probably the way to go.

---

<div class="post-metadata">

**Author:** ![phendric](https://avatars.discourse-cdn.com/v4/letter/p/58f4c7/32.png) [@phendric](https://discourse.julialang.org/u/phendric)\
**Post date:** [December 21, 2020, 4:10pm UTC](https://discourse.julialang.org/t/nested-iterators/52180/5 "2020-12-21T16:10:43Z")

</div>

That’s basically where I started. As I mentioned, I don’t know ahead of time how many repeats I’m going to need.

---

<div class="post-metadata">

**Author:** ![malacroi](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/malacroi/32/19745_2.png) [@malacroi](https://discourse.julialang.org/u/malacroi)\
**Post date:** [December 21, 2020, 4:11pm UTC](https://discourse.julialang.org/t/nested-iterators/52180/6 "2020-12-21T16:11:54Z")

</div>

As per [iterator - N-Dimensional Cartesian Product of a set in Julia - Stack Overflow](https://stackoverflow.com/questions/56120583/n-dimensional-cartesian-product-of-a-set-in-julia), the most compact way to write your desired iterator might be

```julia
Interators.product(ntuple(i->[3,7], 3)...)

```

Splatting the `ntuple` instead of the `Array` produced by `fill` saves the allocation of a temporary `Array` (and is probably a meaningless optimization in the grand scheme of things).

---

<div class="post-metadata">

**Author:** ![phendric](https://avatars.discourse-cdn.com/v4/letter/p/58f4c7/32.png) [@phendric](https://discourse.julialang.org/u/phendric)\
**Post date:** [December 21, 2020, 4:13pm UTC](https://discourse.julialang.org/t/nested-iterators/52180/7 "2020-12-21T16:13:48Z")

</div>

**Splatting** …wasn’t something I knew was a thing until now. I figured there had to be something “opposite” to `collect` but I wasn’t sure what it was.

That solves it; both `Iterators.product(fill([3,7], 3)...)` and `Iterators.product(collect(Iterators.repeated([3, 7], 3))...)` do the thing I expect.

Thanks for teaching me something new today!

---

<div class="post-metadata">

**Author:** ![phendric](https://avatars.discourse-cdn.com/v4/letter/p/58f4c7/32.png) [@phendric](https://discourse.julialang.org/u/phendric)\
**Post date:** [December 21, 2020, 4:24pm UTC](https://discourse.julialang.org/t/nested-iterators/52180/8 "2020-12-21T16:24:05Z")

</div>

Yes, a quick benchmark suggests you’re correct:

With `ntuple`:

```julia
julia> @benchmark a1(1000000)
BenchmarkTools.Trial:
  memory estimate: 274.66 MiB
  allocs estimate: 3000000
  --------------
  minimum time: 79.635 ms (7.19% GC)
  median time: 84.754 ms (7.32% GC)
  mean time: 87.578 ms (7.31% GC)
  maximum time: 144.052 ms (5.13% GC)
  --------------
  samples: 58
  evals/sample: 1

```

With `fill`:

```julia
julia> @benchmark a2(1000000)
BenchmarkTools.Trial:
  memory estimate: 1.31 GiB
  allocs estimate: 20000000
  --------------
  minimum time: 833.216 ms (7.09% GC)
  median time: 835.067 ms (7.13% GC)
  mean time: 841.229 ms (7.02% GC)
  maximum time: 873.048 ms (6.98% GC)
  --------------
  samples: 6
  evals/sample: 1

```

With `Iterators.repeated()`:

```julia
julia> @benchmark a3(1000000)
BenchmarkTools.Trial:
  memory estimate: 1.31 GiB
  allocs estimate: 20000000
  --------------
  minimum time: 894.701 ms (6.87% GC)
  median time: 935.490 ms (6.90% GC)
  mean time: 933.575 ms (6.95% GC)
  maximum time: 960.498 ms (6.70% GC)
  --------------
  samples: 6
  evals/sample: 1

```

General form of the function being benched:

```julia
function a1(it)
  for jj in 1:it
    kk=0
    for ii in Iterators.product(ntuple(i->[3,7], 3)...)
        kk+=1
    end
  end
end

```
