I have a Record struct and a Map struct like:
defmodule Foo.Bar do
defstruct boo: nil, baz: nil
end
defmodule Foo do
require Record
Record.defrecord :bar, Foo.Bar, [boo: nil, baz: nil]
end
I can convert the Record to Map like this:
defp update_map({k, v}, map), do: Map.update!(map, k, fn(_) -> v end)
defp rd2map(rd) do
Foo.bar(rd) |> Enum.reduce(%Foo.Bar{}, &update_map/2)
end
But how can I convert the Map to a Record?
Elixir Records are deprecated. The Record
module that now exists in Elixir is only used for two things:
This means you should probably not be using them unless you are trying to extract record information from an Erlang file.
Regarding your original question, here's how I would convert Erlang Records and Elixir Structs back and forth. Once you realize that a struct is just a Map
that contains __struct__: Foo.Bar
, and that a Record ist just a tuple that starts with {Foo.Bar, ...}
it's pretty straightforward. The only tricky bit is that the information about the record fields is only available at compile time. Therefore, there is no dynamic way of building a record by default. As far as I know, you can only work around this by storing the field definitions somewhere, and use it to generate the struct and record definition. Later, the same source is re-used to build an ordered tuple with default values (i.e. the record). Remember, you really shouldn't use records. So, be warned: ugly hacks ahead ;-)
defmodule Foo.Bar do
@fields [boo: nil, baz: nil]
def fields, do: @fields
defstruct @fields
end
defmodule Foo do
require Record
Record.defrecord :bar, Foo.Bar, Foo.Bar.fields
end
defmodule Foo.Utils do
require Foo
def record_to_struct(record) do
[{:__struct__, Foo.Bar} | Foo.bar(record)] |> Enum.into(%{})
end
def struct_to_record(struct) do
map = Map.from_struct(struct)
for {key, default} <- Foo.Bar.fields, into: [Foo.Bar] do
Dict.get(map, key, default)
end |> List.to_tuple
end
end
All the disclaimers and information by Patrick is correct. You can't solve the problem at runtime without annotating the fields.
You can, however, solve this issue at compile time if you are converting from an Erlang record (which is mostly the only reason for using them). We do it on Elixir source code to convert Erlang's #file_info{}
into %File.Stat{}
:
https://github.com/elixir-lang/elixir/blob/master/lib/elixir/lib/file/stat.ex
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With