How to pattern match this nested structure?

I have this body structure in a POST query:

{
    "id": "019016a1-e00b-7e6f-ab3b-0d55f0c6bb4e",
    "student_id": "01900cd1-ca56-724f-8b9b-fae26272380a",
    "citation": {
        "ISO-0012": {
            "t": "11111111-1574-7646-9264-30e3be99e219",
            "b": "22222222-1574-7646-9264-30e3be99e219"
        }
    }
}

My question, in a controller method, how to match and capture these as variables,
ISO-0012 as citation_id
t as t
b as b
?

citation_id can have any value.

Thank you.

You probably cannot, because the citation_id key is not known at compile time (it is dynamic I guess)

1 Like

So I just have to move it inside the body ? at same level as t and b ?

Yes, as a value, not a key

1 Like

As @kokolegorille says it’s not possible because the key is not known at compile time, but a partial solution would be the following:

json = ~S"""
{
    "id": "019016a1-e00b-7e6f-ab3b-0d55f0c6bb4e",
    "student_id": "01900cd1-ca56-724f-8b9b-fae26272380a",
    "citation": {
        "ISO-0012": {
            "t": "11111111-1574-7646-9264-30e3be99e219",
            "b": "22222222-1574-7646-9264-30e3be99e219"
        }
    }
}
"""

{:ok, parsed} = Jason.decode(json, keys: :strings, strings: :copy)

%{
    "id" => _id,
    "student_id" => _student_id,
    "citation" => citation
} = parsed

citation_id = citation |> Map.keys() |> hd()

%{^citation_id => %{"b" => b, "t" => t}} = citation

And now you have citation_id, t and b holding the right values.

It’s a bit clunky in your scenario but nothing that cannot be neatly wrapped. :slight_smile:

Or you can always use Access, or (what I would do) a chain of Map.fetch calls in a with block. Up to you.

3 Likes

Slightly hacky and breaks if there’s ever multiple keys under the `“citation” map, but for fun:

iex(12)> json = ~S"""
...(12)> {
...(12)>     "id": "019016a1-e00b-7e6f-ab3b-0d55f0c6bb4e",
...(12)>     "student_id": "01900cd1-ca56-724f-8b9b-fae26272380a",
...(12)>     "citation": {
...(12)>         "ISO-0012": {
...(12)>             "t": "11111111-1574-7646-9264-30e3be99e219",
...(12)>             "b": "22222222-1574-7646-9264-30e3be99e219"
...(12)>         }
...(12)>     }
...(12)> }
...(12)> """

iex(14)> {:ok, parsed} = Jason.decode(json, keys: :strings, strings: :copy)

iex(15)> {citation_id, t, b} = Enum.find_value(parsed["citation"], fn {k, %{"t" => t, "b" => b}} -> {k, t, b} end)
{"ISO-0012", "11111111-1574-7646-9264-30e3be99e219",
 "22222222-1574-7646-9264-30e3be99e219"}
iex(16)> citation_id
"ISO-0012"
iex(17)> t
"11111111-1574-7646-9264-30e3be99e219"
iex(18)> b
"22222222-1574-7646-9264-30e3be99e219"
2 Likes