You could use
ntpermutations(nt_in) = [NamedTuple{keys(nt_in)}(reverse(t)) for t in Iterators.product(reverse(nt_in)...)] |> vec
julia> nt_in = (; category = [1, 3, 4], letter = ["a", "b", "c"], score = [1.1, 4.0], thing = ["hat", "cat"])
(category = [1, 3, 4], letter = ["a", "b", "c"], score = [1.1, 4.0], thing = ["hat", "cat"])
julia> nt = ntpermutations(nt_in)
36-element Vector{@NamedTuple{category::Int64, letter::String, score::Float64, thing::String}}:
(category = 1, letter = "a", score = 1.1, thing = "hat")
(category = 1, letter = "a", score = 1.1, thing = "cat")
(category = 1, letter = "a", score = 4.0, thing = "hat")
(category = 1, letter = "a", score = 4.0, thing = "cat")
(category = 1, letter = "b", score = 1.1, thing = "hat")
(category = 1, letter = "b", score = 1.1, thing = "cat")
(category = 1, letter = "b", score = 4.0, thing = "hat")
(category = 1, letter = "b", score = 4.0, thing = "cat")
(category = 1, letter = "c", score = 1.1, thing = "hat")
(category = 1, letter = "c", score = 1.1, thing = "cat")
(category = 1, letter = "c", score = 4.0, thing = "hat")
(category = 1, letter = "c", score = 4.0, thing = "cat")
(category = 3, letter = "a", score = 1.1, thing = "hat")
⋮
(category = 4, letter = "a", score = 1.1, thing = "hat")
(category = 4, letter = "a", score = 1.1, thing = "cat")
(category = 4, letter = "a", score = 4.0, thing = "hat")
(category = 4, letter = "a", score = 4.0, thing = "cat")
(category = 4, letter = "b", score = 1.1, thing = "hat")
(category = 4, letter = "b", score = 1.1, thing = "cat")
(category = 4, letter = "b", score = 4.0, thing = "hat")
(category = 4, letter = "b", score = 4.0, thing = "cat")
(category = 4, letter = "c", score = 1.1, thing = "hat")
(category = 4, letter = "c", score = 1.1, thing = "cat")
(category = 4, letter = "c", score = 4.0, thing = "hat")
(category = 4, letter = "c", score = 4.0, thing = "cat")
If you don’t care about the order, you can remove the reverse
s.
Explanation
Iterators.product(reverse(nt_in)...)
creates an iterator containing all possible tuples whose entries are taken from the elements in the fields of nt_in
. The first axis in Iterators.product
will cycle the fastest. Thanks to the reverse
, nt_in
’s first field (category) is put at the back and hence changes slowest.
NamedTuple{keys(nt_in)}(reverse(t))
takes such a tuple t
and converts it in a NamedTuple
with the same names as nt_in
. We have to undo the reverse from before.
|> vec
: Iterators.product
produces an n
-dimensional structure, where n = length(nt_in)
. The list comprehension then creates an Array{@NamedTuple{...}, n}
, which we flatten to a Vector
.