Here is example working code:
# First of all minimal definition in order to make code compillable
defmodule Nadia.Model.Chat do
defstruct [:first_name, :id, :last_name, :photo, :title, :type, :username]
end
defmodule Nadia.Model.User do
defstruct [:first_name, :id, :last_name, :username]
end
defmodule Nadia.Model.Message do
defstruct [
:audio,
:caption,
:channel_chat_created,
:chat,
:contact,
:date,
:delete_chat_photo,
:document,
:edit_date,
:entities,
:forward_date,
:forward_from,
:forward_from_chat,
:from,
:group_chat_created,
:left_chat_member,
:location,
:message_id,
:migrate_from_chat_id,
:migrate_to_chat_id,
:new_chat_member,
:new_chat_photo,
:new_chat_title,
:photo,
:pinned_message,
:reply_to_message,
:sticker,
:supergroup_chat_created,
:text,
:venue,
:video,
:voice
]
end
# Here is code which would work only for your specific case
defmodule Example do
def sample(map) do
message = ensure_map(map.message)
chat = ensure_map(message.chat)
from = ensure_map(message.from)
updated_message = %{message | chat: chat, from: from}
%{map | message: updated_message}
end
defp ensure_map(%{__struct__: _} = struct), do: Map.from_struct(struct)
defp ensure_map(data), do: data
end
# Here is more dynamic way
# No matter how many maps and lists have you nested
# It will iterate over all map or list elements and finally ensure that they are not structs
# NOTE: If you want you can add extra guard to limit possible __struct__ value
# For example you probably do not want to create maps from Date, DateTime, NaiveDateTime and Time structs
defmodule ExampleDynamic do
def sample(map), do: :maps.map(&do_sample/2, map)
def do_sample(_key, value), do: ensure_nested_map(value)
defp ensure_nested_map(list) when is_list(list), do: Enum.map(list, &ensure_nested_map/1)
# NOTE: In pattern-matching order of function guards is important!
# @structs [Date, DateTime, NaiveDateTime, Time]
# defp ensure_nested_map(%{__struct__: struct} = data) when struct in @structs, do: data
defp ensure_nested_map(%{__struct__: _} = struct) do
map = Map.from_struct(struct)
:maps.map(&do_sample/2, map)
end
defp ensure_nested_map(data), do: data
end
# Your example data which is needed for compilation
data = %{
callback_query: nil,
channel_post: nil,
chosen_inline_result: nil,
edited_message: nil,
inline_query: nil,
message: %Nadia.Model.Message{
audio: nil,
caption: nil,
channel_chat_created: nil,
chat: %Nadia.Model.Chat{
first_name: "Christian",
id: 543_211_234,
last_name: "Tovar",
photo: nil,
title: nil,
type: "private",
username: "ChristianTovar"
},
contact: nil,
date: 1_562_605_521,
delete_chat_photo: nil,
document: nil,
edit_date: nil,
entities: nil,
forward_date: nil,
forward_from: nil,
forward_from_chat: nil,
from: %Nadia.Model.User{
first_name: "Christian",
id: 543_211_234,
last_name: "Tovar",
username: "ChristianTovar"
},
group_chat_created: nil,
left_chat_member: nil,
location: nil,
message_id: 714,
migrate_from_chat_id: nil,
migrate_to_chat_id: nil,
new_chat_member: nil,
new_chat_photo: [],
new_chat_title: nil,
photo: [],
pinned_message: nil,
reply_to_message: nil,
sticker: nil,
supergroup_chat_created: nil,
text: "google meets",
venue: nil,
video: nil,
voice: nil
},
update_id: 412_827_321
}
# Here we are ensuring that both ways gives exactly same results
Example.sample(data) == ExampleDynamic.sample(data)
Please let me know if you have any questions.
Related documentation:
- &:maps.map/2
- &Enum.map/2
- &Map.from_struct/1
- Guards
- Map (describes specific update syntax)