# Product iterator with NamedTuples

Hi all,

I was wondering whether it is possible to create a product iterator consisting of NamedTuples. The product iterator generates tuples like so:

``````Base.product(1:3,4:5) |> collect
3×2 Matrix{Tuple{Int64, Int64}}:
(1, 4)  (1, 5)
(2, 4)  (2, 5)
(3, 4)  (3, 5)
``````

What I would like to do is associate each element with a keyword, such as:

`````` (a=1, b=4)  (a=1, b=5)
(a=2, b=4)  (a=2, b=5)
(a=3, b=4)  (a=3, b=5)
``````

Is this possible?

Is this generic enough?

``````julia> abTuple(t) = (; zip((:a, :b), t)...)
abTuple (generic function with 1 method)

julia> Base.product(1:3,4:5) .|> abTuple
3×2 Matrix{NamedTuple{(:a, :b), Tuple{Int64, Int64}}}:
(a = 1, b = 4)  (a = 1, b = 5)
(a = 2, b = 4)  (a = 2, b = 5)
(a = 3, b = 4)  (a = 3, b = 5)
``````
1 Like

Yes. Thank you. This will work for my use case. I suppose one way to make it more generic is to create a custom iterator.

Another way might be to broadcast the constructor, as

``````julia> it = Base.product(1:3, 4:5);

julia> @NamedTuple{a::Int, b::Int}.(it)
3×2 Matrix{NamedTuple{(:a, :b), Tuple{Int64, Int64}}}:
(a = 1, b = 4)  (a = 1, b = 5)
(a = 2, b = 4)  (a = 2, b = 5)
(a = 3, b = 4)  (a = 3, b = 5)
``````

A lazy version might be

``````julia> g = (@NamedTuple{a::Int, b::Int}(x) for x in it)

julia> collect(g)
3×2 Matrix{NamedTuple{(:a, :b), Tuple{Int64, Int64}}}:
(a = 1, b = 4)  (a = 1, b = 5)
(a = 2, b = 4)  (a = 2, b = 5)
(a = 3, b = 4)  (a = 3, b = 5)
``````
4 Likes

For something more than an iterator, see (my) RectiGrids.jl package:

``````julia> using RectiGrids

# create a grid with named dimensions:
julia> G = grid(a=1:3, b=4:5)
2-dimensional KeyedArray(NamedDimsArray(...)) with keys:
↓   a ∈ 3-element UnitRange{Int64}
→   b ∈ 2-element UnitRange{Int64}
And data, 3×2 RectiGrids.RectiGridArr{(:a, :b), NamedTuple{(:a, :b), Tuple{Int64, Int64}}, 2, Tuple{Nothing, Nothing}, Tuple{UnitRange{Int64}, UnitRange{Int64}}}:
(4)                (5)
(1)     (a = 1, b = 4)     (a = 1, b = 5)
(2)     (a = 2, b = 4)     (a = 2, b = 5)
(3)     (a = 3, b = 4)     (a = 3, b = 5)

# it behaves as an array, with NamedTuple elements:
julia> G[1, 2]
(a = 1, b = 5)

# even more, a grid is a KeyedArray and can use any of KeyedArray selectors
# this still doesn't materialize the whole grid:
julia> G(a = >(1))  # only keep values with a > 1
2-dimensional KeyedArray(NamedDimsArray(...)) with keys:
↓   a ∈ 2-element UnitRange{Int64}
→   b ∈ 2-element UnitRange{Int64}
And data, 2×2 view(::RectiGrids.RectiGridArr{(:a, :b), NamedTuple{(:a, :b), Tuple{Int64, Int64}}, 2, Tuple{Nothing, Nothing}, Tuple{UnitRange{Int64}, UnitRange{Int64}}}, 2:3, :) with eltype NamedTuple{(:a, :b), Tuple{Int64, Int64}}:
(4)                (5)
(2)     (a = 2, b = 4)     (a = 2, b = 5)
(3)     (a = 3, b = 4)     (a = 3, b = 5)

# applying a function over the grid keeps the keyed indices (a and b):
julia> map(x -> x.a^2 + x.b, G)
2-dimensional KeyedArray(NamedDimsArray(...)) with keys:
↓   a ∈ 3-element UnitRange{Int64}
→   b ∈ 2-element UnitRange{Int64}
And data, 3×2 Matrix{Int64}:
(4)  (5)
(1)    5    6
(2)    8    9
(3)   13   14
``````
1 Like

Awesome. Thank you for all of the ideas!