'Elixir: best practice to extract data from nested structs
In Elixir we can get data from nested data structures using
data = %{field: %{other_field: 1}}
data[:field][:other_field]
If it contains lists it also can be done using
data = %{field: %{other_field: [1]}}
get_in data, [:field, :other_field, Access.at(0)]
But how to get that data given that data.field.other_field is a structure?
Both of the above would fail because structs don't implement Access.fetch/2.
data = %{field: %{other_field: %Struct{a: 1}}}
So what's the right way to access nested structs data other than pattern matching?
Solution 1:[1]
I use my implementation of ruby's try operator for this.
@spec try(Access.t(), nonempty_list(node) | atom, term) :: term
def try(data, keys, default \\ nil)
def try(nil, _keys, default), do: default
def try(data, keys, default) when is_atom(keys) do
try(data, [keys], default)
end
def try(data, keys, default) when length(keys) == 1 do
get_in(data, [Access.key(hd(keys), default)])
end
def try(data, keys, default) do
get_in(data, [Access.key(hd(keys))])
|> try(tl(keys), default)
end
Usage in your example
try(data, [:field, :other_field, :a]) # with default nil
try(data, [:field, :other_field, :a], 42) # with default 42
try(data, :single_field_as_atom) # with a single value
Solution 2:[2]
A generic implementation is provided in the Accessible and StructAccess libraries.
This allows you to use the familiar Map syntax.
data = %Data{field: %{other_field: 1}}
data[:field][:other_field]
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | cmo |
