Elixir SlackとElixir JP Slackを毎日眺めるのを日課にしています
そこでいろんなアイデアを得ることがあるのですが、今日は一つご紹介します
やりたいこと
構造体とMapが深いネストで同居していている場合に、全てMapにしたい
架空の構造体
こういう構造体があったとします
defmodule Hello, do: defstruct [:x]
最上階層のみを構造体からMapへ変換
iex> s = %Hello{x: %Hello{x: 1}}
iex> Map.from_struct(s)
%{x: %Hello{x: 1}}
ネスト構造の中のすべての構造体をMapへ変換
defmodule Mnishiguchi.Map do
def unstruct(%NaiveDateTime{} = encodable_struct), do: encodable_struct
def unstruct(%DateTime{} = encodable_struct), do: encodable_struct
def unstruct(%Time{} = encodable_struct), do: encodable_struct
def unstruct(%Date{} = encodable_struct), do: encodable_struct
def unstruct(struct_or_map) when is_map(struct_or_map) do
struct_or_map
|> Map.delete(:__struct__)
|> Map.delete(:__meta__)
|> Map.new(fn
{k, v} when is_map(v) -> {k, unstruct(v)}
{k, v} when is_list(v) -> {k, Enum.map(v, &unstruct/1)}
kv -> kv
end)
end
def unstruct(not_struct_or_map) do
not_struct_or_map
end
end
iex> s = %Hello{x: %Hello{x: %Hello{x: [%Hello{x: 1}, %Hello{x: %Hello{x: 1}}]}}}
iex> Mnishiguchi.Map.unstruct(s)
%{x: %{x: %{x: [%{x: 1}, %{x: %{x: 1}}]}}}
Mapと構造体は若干異なる
ElixirのMapと構造体はほぼ同じなのですが、挙動が若干異なります
違いの一つにAccess behaviour
が挙げられます
構造体はAccess behaviour
を実装していないので、[]
でアクセスしようとするとUndefinedFunctionError
になります
# Map
iex> m = %{x: 1}
iex> m.x
1
iex> m[:x]
1
# 構造体
iex> s = %Hello{x: 1}
iex> s.x
1
iex> s[:x]
** (UndefinedFunctionError) function Hello.fetch/2 is undefined (Hello does not implement the Access behaviour)
Hello.fetch(%Hello{x: 1}, :x)
(elixir 1.12.2) lib/access.ex:285: Access.get/3