2015-03-06 14 views
5

Tôi có một Ghi struct và một bản đồ cấu trúc như:Elixir Làm thế nào để chuyển đổi một bản đồ struct lên mức kỷ lục struct

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 

tôi có thể chuyển đổi các ghi vào Map như thế này:

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 

Nhưng làm thế nào tôi có thể chuyển đổi Bản đồ thành Bản ghi?

+0

Đối với những người có thể thấy câu hỏi này và lựa chọn nào tốt: hồ sơ Elixir đang bị phản đối. Họ chỉ có mặt để cho phép Elixir làm việc với các hồ sơ Erlang. –

Trả lời

10

Elixir Records are deprecated. Các module Record mà bây giờ tồn tại trong Elixir chỉ được sử dụng cho hai điều:

  1. để làm việc với ngắn, dữ liệu nội bộ
  2. để giao tiếp với các hồ sơ Erlang

này có nghĩa là bạn nên có lẽ không được sử dụng trừ khi bạn đang cố gắng trích xuất thông tin bản ghi từ tệp Erlang.

Về câu hỏi ban đầu của bạn, dưới đây là cách tôi sẽ chuyển đổi Erlang Records và Elixir Struct lui. Một khi bạn nhận ra rằng một cấu trúc chỉ là một Map chứa __struct__: Foo.Bar, và rằng một Ghi ist chỉ là một tuple mà bắt đầu với {Foo.Bar, ...} nó khá đơn giản. Bit khó khăn duy nhất là thông tin về các trường bản ghi chỉ có sẵn tại thời gian biên dịch. Do đó, theo mặc định, không có cách tạo động nào để tạo bản ghi. Theo như tôi biết, bạn chỉ có thể làm việc xung quanh điều này bằng cách lưu trữ các định nghĩa trường một nơi nào đó, và sử dụng nó để tạo ra cấu trúc và định nghĩa bản ghi. Sau đó, cùng một nguồn được sử dụng lại để tạo một bộ dữ liệu có thứ tự với các giá trị mặc định (tức là bản ghi). Hãy nhớ rằng, bạn thực sự không được sử dụng hồ sơ. Vì vậy, được cảnh báo: hacks xấu xí phía trước ;-)

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 
+0

Edit: Tôi loại bỏ các mô-đun helper thêm và thêm 'Foo.Bar.fields/1' để thay thế. Bên trong module 'Foo.Bar', chúng ta có thể sử dụng thuộc tính module' @ fields'. –

+0

Nó được coi là phong cách tốt để dựa vào các đại diện tuple của hồ sơ trong Erlang/Elixir? – Flow

+0

Tôi muốn nói điều đó tùy thuộc vào bạn. Đối với các bộ dữ liệu rất đơn giản như ngày/giờ từ erlang, tôi sẽ sử dụng chúng trực tiếp vì chúng nhỏ và không thay đổi được. Đối với bất cứ điều gì phức tạp hơn, hoặc tùy thuộc vào những thay đổi trong tương lai, tôi sẽ dính withe phương pháp mà @ JoseValim liên kết đến trong câu trả lời của mình. –

2

Tất cả các tuyên bố từ chối trách nhiệm và thông tin của Patrick là chính xác. Bạn không thể giải quyết vấn đề khi chạy mà không chú thích các trường.

Bạn có thể, tuy nhiên, giải quyết vấn đề này tại thời gian biên dịch nếu bạn đang chuyển đổi từ một bản ghi Erlang (mà chủ yếu là lý do duy nhất để sử dụng chúng). Chúng tôi làm điều đó trên mã nguồn Elixir để chuyển đổi Erlang của #file_info{} vào %File.Stat{}:

https://github.com/elixir-lang/elixir/blob/master/lib/elixir/lib/file/stat.ex

Các vấn đề liên quan