2015-03-06 7 views
5

ich eine Rekord-Struktur und eine Map-Struktur wie konvertieren:Elixir Wie man eine Map-Struktur auf einen Rekord 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 

ich den Rekord konvertieren kann Karte wie folgt aus:

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 

Aber Wie kann ich die Karte in einen Datensatz konvertieren?

+0

Für diejenigen, die diese Frage sehen könnten, und suchen Sie nicht weiter: Elixier Aufzeichnungen sind veraltet. Sie sind nur vorhanden, damit Elixir mit Erlang-Aufzeichnungen arbeiten kann. –

Antwort

10

Elixir Records are deprecated. Das Record-Modul, das in Elixir existiert jetzt nur für zwei Dinge verwendet:

  1. mit kurzen, internen Daten zu arbeiten
  2. Schnittstelle mit Erlang Aufzeichnungen

Dies bedeutet, sollten Sie wahrscheinlich nicht verwenden wenn Sie nicht versuchen, Datensatzinformationen aus einer Erlang-Datei zu extrahieren.

In Bezug auf Ihre ursprüngliche Frage, hier ist, wie ich Erlang Records und Elixir Structs hin und her konvertieren würde. Sobald Sie erkennen, dass eine Struktur nur eine Map ist, die __struct__: Foo.Bar enthält, und dass ein Record nur ein Tupel ist, das mit {Foo.Bar, ...} beginnt, ist es ziemlich einfach. Das einzige knifflige Bit ist, dass die Informationen zu den Datensatzfeldern nur zur Kompilierzeit verfügbar sind. Daher gibt es keine dynamische Möglichkeit, einen Datensatz standardmäßig zu erstellen. Soweit ich weiß, können Sie das nur umgehen, indem Sie die Felddefinitionen irgendwo speichern und sie zum Generieren der Struktur- und Datensatzdefinition verwenden. Später wird dieselbe Quelle wiederverwendet, um ein geordnetes Tupel mit Standardwerten (d. H. Dem Datensatz) zu erzeugen. Denken Sie daran, Sie wirklich sollten keine Datensätze verwenden. Also, sei gewarnt: hässliche Hacks voraus ;-)

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: Ich habe das zusätzliche Hilfsmodul eliminiert und 'Foo.Bar.fields/1' stattdessen hinzugefügt. Im Modul 'Foo.Bar' können wir das Modulattribut' @ fields' verwenden. –

+0

Wird es als guter Stil angesehen, sich auf Tupelrepräsentationen von Datensätzen in Erlang/Elixir zu verlassen? – Flow

+0

Ich würde es dir sagen. Für sehr einfache Tupel wie Datum/Zeit von Erlang würde ich sie direkt verwenden, da sie ziemlich klein sind und sich kaum ändern. Für etwas Komplizierteres oder zukünftige Änderungen würde ich an der Methode festhalten, mit der @JoseValim in seiner Antwort verknüpft ist. –

2

Alle Disclaimer und Informationen von Patrick ist korrekt. Sie können das Problem zur Laufzeit nicht lösen, ohne die Felder zu kommentieren.

Sie können dieses Problem jedoch zur Kompilierzeit lösen, wenn Sie von einem Erlang-Datensatz konvertieren (was meistens der einzige Grund für deren Verwendung ist). Wir tun es auf Elixir Quellcode Erlang #file_info{} in %File.Stat{} zu konvertieren:

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

Verwandte Themen