# Filling an array with a generator that returns tuples

**URL:** <https://discourse.julialang.org/t/filling-an-array-with-a-generator-that-returns-tuples/62737>\
**Category:** General Usage\
**Tags:** performance, array, tuple, memory-allocation, generator\
**Created:** [June 11, 2021, 12:54pm UTC](https://discourse.julialang.org/t/filling-an-array-with-a-generator-that-returns-tuples/62737 "2021-06-11T12:54:19Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![fedario](https://avatars.discourse-cdn.com/v4/letter/f/f14d63/32.png) [@fedario](https://discourse.julialang.org/u/fedario)\
**Post date:** [June 11, 2021, 12:54pm UTC](https://discourse.julialang.org/t/filling-an-array-with-a-generator-that-returns-tuples/62737/1 "2021-06-11T12:54:20Z")

</div>

I’m trying to create a function that takes a number of iterables and fills an array columnwise with each combination of those. The following function has the correct output, but the number of allocations is unexpected.

```julia
using Base.Iterators
using BenchmarkTools

function product_stack(iters...)
    output = zeros(length(iters), (*).(length.(iters)...))
    for (index, combination) in enumerate(product(iters...))
        output[:, index] .= combination
    end
    return output
end

```

```julia
julia> @btime product_stack(1:3, 1:3)
  7.512 μs (91 allocations: 3.69 KiB)
2×9 Matrix{Float64}:
 1.0 2.0 3.0 1.0 2.0 3.0 1.0 2.0 3.0
 1.0 1.0 1.0 2.0 2.0 2.0 3.0 3.0 3.0

```

**Is it possible to do this with a single allocation?**

Extra question:  
I was hoping something like this might work, but unfortunately it does not:

```julia
function product_stack(iters...)
    output = zeros(length(iters), (*).(length.(iters)...))
    output .= product(iters...)
    return output
end

```

Why not?

---

<div class="post-metadata">

**Author:** ![Seif\_Shebl](https://avatars.discourse-cdn.com/v4/letter/s/eada6e/32.png) [@Seif\_Shebl](https://discourse.julialang.org/u/Seif_Shebl)\
**Post date:** [June 11, 2021, 10:47pm UTC](https://discourse.julialang.org/t/filling-an-array-with-a-generator-that-returns-tuples/62737/2 "2021-06-11T22:47:40Z")

</div>

This is much faster:

```julia
function product_stack2(iters...)
    p = flatten(product(iters...))
    reshape(collect(p), length(iters), :)
end

@btime product_stack2(1:3,1:3)
  101.695 ns (3 allocations: 320 bytes)
2×9 Matrix{Int64}:
 1 2 3 1 2 3 1 2 3
 1 1 1 2 2 2 3 3 3

```

> [@fedario](#):
>
> but unfortunately it does not:

The second example doesn’t work because when you say `output .= x`, this replaces all elements of `output` with the same `x` element. In your case, `x` is an iterator and each element of `output` is a `Float64`, they can’t match. You can modify it as follows but it won’t be as fast as the version above:

```julia
function product_stack(iters...)
    output = zeros(Int64, length(iters), (*).(length.(iters)...))
    output[1:end] .= flatten(product(iters...))
    return output
end
@btime product_stack(1:3,1:3)
  1.560 μs (17 allocations: 1.02 KiB)

```

---

<div class="post-metadata">

**Author:** ![fedario](https://avatars.discourse-cdn.com/v4/letter/f/f14d63/32.png) [@fedario](https://discourse.julialang.org/u/fedario)\
**Post date:** [June 12, 2021, 10:38am UTC](https://discourse.julialang.org/t/filling-an-array-with-a-generator-that-returns-tuples/62737/3 "2021-06-12T10:38:53Z")

</div>

Wow, thank you for your answer!  
That broadened my horizon regarding iterators and especially reshape.

---

<div class="post-metadata">

**Author:** ![rocco\_sprmnt21](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rocco_sprmnt21/32/20127_2.png) [@rocco\_sprmnt21](https://discourse.julialang.org/u/rocco_sprmnt21)\
**Post date:** [December 31, 2021, 8:45pm UTC](https://discourse.julialang.org/t/filling-an-array-with-a-generator-that-returns-tuples/62737/4 "2021-12-31T20:45:36Z")

</div>

I stumbled upon this old post and fished out some scripts I had made long ago for a similar goal.  
Stimulated by @Seif_Shebl 's brilliant solution, I tried to find a new one that has comparable performance

```julia
julia> @btime hcat(collect.(Base.Iterators.product(1:3,1:3))...)
       @btime mapreduce(collect, hcat, Base.Iterators.product(1:3,1:3))
       @btime reduce(hcat, collect.(Base.Iterators.product(1:3,1:3)));
  772.642 ns (16 allocations: 1.55 KiB)
  516.667 ns (17 allocations: 1.89 KiB)
  610.286 ns (19 allocations: 2.22 KiB)

julia> @btime reshape(collect(Iterators.flatten(Base.Iterators.product(1:3,1:3))),2,:);
  94.280 ns (3 allocations: 304 bytes)

julia>
julia> @btime reinterpret(reshape,Int,foldr(prod_tuples, (1:3,1:3)))
  34.980 ns (1 allocation: 208 bytes)
2×9 reinterpret(reshape, Int64, ::Vector{Tuple{Int64, Int64}}) with eltype Int64:
 1 1 1 2 2 2 3 3 3
 1 2 3 1 2 3 1 2 3

#with

function prod_tuples(itrs1,itrs2)
l1,l2 = length(itrs1), length(itrs2)
n=length(itrs1[1])+length(itrs2[1])
m=Array{NTuple{n,Int64},1}(undef,l1*l2)
for (i,ei) in enumerate(itrs1)
    for (j,ej) in enumerate(itrs2)
        m[j+l2*(i-1)]=(ei,ej...)
    end
end
m
end

```

Another solution can be obtained by exploiting the possibilities of Cartesian indices

---

<div class="post-metadata">

**Author:** ![rocco\_sprmnt21](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rocco_sprmnt21/32/20127_2.png) [@rocco\_sprmnt21](https://discourse.julialang.org/u/rocco_sprmnt21)\
**Post date:** [January 2, 2022, 8:03am UTC](https://discourse.julialang.org/t/filling-an-array-with-a-generator-that-returns-tuples/62737/5 "2022-01-02T08:03:01Z")

</div>

```julia
       function prod_itrs(itrs)
           foldr((itr1,itr2)->((e1,e2...) for e2 in itr2 for e1 in itr1), itrs)
       end

```

```julia
julia> @btime prod_itrs((1:3,1:3,:5:7)) 0.001 ns (0 allocations: 0 bytes)

```
