How to create permutation of named tuple values

how can I write a generic function to generate permutations of arbitrary named tuple values

this is what I currently do

    catagory0 = [1,3,4]
    letter0 = ["a", "b", "c"]
    score0 = [1.1, 4.]
    thing0 = ["hat", "cat"]

    nt = NamedTuple[]
    for catagory in catagory0
        for letter in letter0
            for score in score0
                for thing in thing0
                    push!(nt, (;catagory, letter, score, thing))
                end
            end
        end
    end

    julia> nt
    36-element Vector{NamedTuple}:
    (catagory = 1, letter = "a", score = 1.1, thing = "hat")
    (catagory = 1, letter = "a", score = 1.1, thing = "cat")
    (catagory = 1, letter = "a", score = 4.0, thing = "hat")
    (catagory = 1, letter = "a", score = 4.0, thing = "cat")
    (catagory = 1, letter = "b", score = 1.1, thing = "hat")
    ⋮
    (catagory = 4, letter = "c", score = 1.1, thing = "hat")
    (catagory = 4, letter = "c", score = 1.1, thing = "cat")
    (catagory = 4, letter = "c", score = 4.0, thing = "hat")
    (catagory = 4, letter = "c", score = 4.0, thing = "cat")

I would like to write a generic function ntpermutations that does the above but accepts any named tuple of values

  nt_in = (;catagory = [1, 3, 4], letter = ["a", "b", "c"], score = [1.1, 4.0], thing = ["hat", "cat"])
  nt = ntpermutations(nt_in)

where in this case nt == nt from first example

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 reverses.

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.
1 Like

in a single line nonetheless! Works like a charm.

1 Like