'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