9

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?

3 Answers 3

23

Use Access.key/2:

key(key, default \\ nil)

Accesses the given key in a map/struct.

Uses the default value if the key does not exist or if the value being accessed is nil.

iex(1)> defmodule Struct do
...(1)>   defstruct [:a]
...(1)> end
iex(2)> data = %{field: %{other_field: %Struct{a: 1}}}
%{field: %{other_field: %Struct{a: 1}}}
iex(3)> get_in data, [:field, :other_field, Access.key(:a)]
1
iex(4)> get_in data, [:field, :other_field, Access.key(:b, :default)]
:default
Sign up to request clarification or add additional context in comments.

Comments

2

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

Comments

0

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]

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.