Wie initialisieren Sie in Elixir eine Struktur mit einer Map-Variablen?

72

Ich weiß, dass es möglich ist, eine Struktur über zu erstellen %User{ email: '[email protected]' }. Aber wenn ich eine Variable hätte, params = %{email: '[email protected]'}gibt es eine Möglichkeit, diese Struktur mit dieser Variablen zu erstellen, z %User{ params }.

Dies gibt einen Fehler aus und fragt sich nur, ob Sie ihn explodieren können oder auf andere Weise?

Ochsenfrosch
quelle

Antworten:

112

Sie sollten die struct/2Funktion verwenden. Aus den Dokumenten:

defmodule User do
  defstruct name: "john"
end

struct(User)
#=> %User{name: "john"}

opts = [name: "meg"]
user = struct(User, opts)
#=> %User{name: "meg"}

struct(user, unknown: "value")
#=> %User{name: "meg"}
José Valim
quelle
4
Wenn Sie Schlüssel erzwungen haben und sicherstellen möchten, dass sie in der Karte vorhanden sind, verwenden Sie struct!/2stattdessen.
RusinaRange
4
Ich bin verwirrt. Diese Antwort zeigt die Initialisierung einer Struktur aus einer Schlüsselwortliste. Aber die Frage fragt nach Struktur aus einer Karte.
Ahmad Ferdous
2
Ich habs! struct/2funktioniert auch für eine Karte. Also opts = %{name: "meg"}funktioniert.
Ahmad Ferdous
27

Die vorherigen Antworten sind beide gut, mit einer Einschränkung: Die Schlüssel in der Struktur sind Atome, die Schlüssel in Ihrem Hash können Zeichenfolgen sein. Bei Verwendung der struct () -Methode werden nur die übereinstimmenden Schlüssel kopiert, und Zeichenfolgen stimmen nicht mit den Atomen überein. Beispiel:

defmodule User do
  defstruct name: "john"
end

opts = %{"name" => "meg"}
user = struct(User, opts)
#=> %User{name: "john"}

Die Verwendung von Merge ist ebenfalls seltsam, da dadurch die Struktur der Map "rückgängig gemacht" wird:

user = Map.merge(%User{}, opts)
#=> %{:__struct__ => User, :name => "john", "name" => "meg"}

Fand dies in der elixir-lang-talk Google Group von Jose selbst:

https://groups.google.com/d/msg/elixir-lang-talk/6geXOLUeIpI/L9einu4EEAAJ

Das ist so ziemlich der richtige Weg, außer dass Sie alles in einem Durchgang erledigen können:

def to_struct(kind, attrs) do
  struct = struct(kind)
  Enum.reduce Map.to_list(struct), struct, fn {k, _}, acc ->
    case Map.fetch(attrs, Atom.to_string(k)) do
      {:ok, v} -> %{acc | k => v}
      :error -> acc
    end
  end
end
dotdotdotPaul
quelle
1
Joses Antwort (und Ihre durch Abzug) sollte die akzeptierte sein, weil sie von Jose stammt (Scherz). Die Verwendung von Merge hat mir Probleme bereitet, und ja, Sie verlieren die Struktur vollständig.
SixFingers
Vielen Dank dafür, ich brauchte eine ziemlich allgemeine Methode, um Strukturen aus allen Arten von Karten zu initialisieren, und dies war die Grundlage meines Hacks, um dies zu tun. Github.com/chrisjowen/ExMapper
Owen