Skip to content

Instantly share code, notes, and snippets.

@mrezentes
Last active March 1, 2024 12:35
Show Gist options
  • Save mrezentes/74ec12bd11c135289d5c05eea2a60a26 to your computer and use it in GitHub Desktop.
Save mrezentes/74ec12bd11c135289d5c05eea2a60a26 to your computer and use it in GitHub Desktop.
Convert a nested struct to a map

Problem: Convert nested structs or nested encoded maps into a decoded map and remove keys with nil values.

Solution: Walk the structure and convert all structures to decoded map.

Before (nested struct):

%TopLevelStuct{
      _type: "Purchase",
      account_key: "ZBmrcY7sv47nCaZOx5TXBTJuazy"
      payment_method_key: "Qg9SqEaGl909ulsNhzrzJsBYSYm",
      payment_method_snapshot: %NestedStruct{
        _type: nil,
        card_type: "master"}
}

After (Struct to Map):

%{
      _type: "Purchase",
      account_key: "ZBmrcY7sv47nCaZOx5TXBTJuazy",
      payment_method_key: "Qg9SqEaGl909ulsNhzrzJsBYSYm",
      payment_method_snapshot: %{card_type: "master"}
}
defmodule MapMaker do

  def convert_to_map!(nil)  do
    nil
  end

  def convert_to_map!(attribute) when is_map(attribute) do
    ensure_struct_is_converted_to_map(attribute)
    |> Map.keys
    |> Enum.reduce(%{}, fn(key, new_map) ->
      update_map(new_map, key, convert_to_map!(Map.get(attribute,key)))
     end)
    |> convert_map_keys_to_atoms
    |> check_for_empty_map
  end

  def convert_to_map!(attribute) when is_binary(attribute) do
    case Poison.decode(attribute) do
      {:ok, decoded_attribute=%{}} -> convert_to_map!(decoded_attribute)
      {:ok, decoded_attribute} -> decoded_attribute
      {:error, _ } -> attribute
      {:error, _ , _} -> attribute
    end
  end

  def convert_to_map!(attribute) do
    attribute
  end

  def convert_map_keys_to_atoms(map) do
    map
    |> Enum.reduce(%{}, fn ({key, val}, acc) -> Map.put(acc, ensure_key_is_atom(key), val) end)
  end

  def ensure_key_is_atom(key) when is_binary(key) do
    String.to_atom(key)
  end

  def ensure_key_is_atom(key) when is_atom(key) do
    key
  end

  defp ensure_struct_is_converted_to_map(struct=%{__struct__: _}) do
    Map.from_struct(struct)
  end

  defp ensure_struct_is_converted_to_map(map) do
    map
  end

  defp update_map(map, _key, _value = nil), do: map
  defp update_map(map, key, value), do: Map.put(map, key, value)

  defp check_for_empty_map(map) when map == %{}, do: nil
  defp check_for_empty_map(map), do: map

end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment