简体   繁体   English

lix剂用另一个结构初始化结构

[英]elixir initialise struct with another struct

What if I have the following struct: 如果我具有以下结构怎么办:

%Registration{email: "john@gmail.com", first_name: "John", last_name: "Doe"}

And I want to create two different structs from it: 我想从中创建两个不同的结构:

%Account{email: "john@gmail.com", password: nil, ...}
%Profile{first_name: "John", last_name: "Doe", age: nil, ...}

I have already taken a look at this question , but if I do 我已经看过这个问题 ,但是如果这样做

registration = %Registration{email: "john@gmail.com", first_name: "John", last_name: "Doe"}
struct(Account, registration)

I receive protocol Enumerable not implemented for %Registration{... 我收到protocol Enumerable not implemented for %Registration{...

So I have to use Map.from_struct 所以我必须使用Map.from_struct

registration = %Registration{email: "john@gmail.com", first_name: "John", last_name: "Doe"}
struct(Account, Map.from_struct(registration))

But I think it is not very clean. 但是我认为它不是很干净。 So I was just thinking is there a better way to do this? 所以我只是在想是否有更好的方法可以做到这一点? This should be quite a common problem, right? 这应该是一个很普遍的问题,对吧?

As per the documentation: 根据文档:

Converts a struct to map. 将结构转换为地图。 It accepts the struct module or a struct itself and simply removes the struct field from the struct. 它接受struct模块或结构本身,并简单地从struct中删除struct字段。

The function exists for a reason. 该功能存在是有原因的。 I don't see the way you did it as being a problem really. 我不认为您的做法确实是个问题。 If it is a common problem I haven't seen it before. 如果是常见问题,我以前从未见过。 If you want to have a more expressive or cleaner code you may try: 如果您想拥有更具表现力或更简洁的代码,可以尝试:

registration = %Registration{email: "john@gmail.com", first_name: "John", last_name: "Doe"} |> Map.from_struct
struct(Account, registration)

or other equivalent code by checking the docs here . 或其他等效代码(通过检查这里的文档)。

You can do it easily, but in a bit hacky way - just delegate protocol functions to Enumerable.Map : 您可以轻松地做到这一点,但是要有点笨拙-只需将协议函数委托给Enumerable.Map


  defmodule Contact do
    defstruct [:name, group: "", version: "", status: :latent]

    defimpl Enumerable, for: Contact do
      defdelegate([count(e), member?(e, m), reduce(e, a, f)], 
                  to: Enumerable.Map)
    end
  end

or in a bit more proper way like 或者以更合适的方式


  defmodule Contact do
    defstruct [:name, group: "", version: "", status: :latent]

    defimpl Enumerable do
      alias Enumerable, as: E
      def count(c), do: Map.from_struct(c) |> E.count()
      def member?(c, m), do: Map.from_struct(c) |> E.member?(m)
      def reduce(c, a, f), do: Map.from_struct(c) |> E.reduce(a, f)
    end
  end

Enumerable.count implementation can be generated with macro to return struct members count which is already known at compile time. 可以使用宏生成Enumerable.count实现,以返回在编译时已知的结构成员计数。 Enumerable.member? can also be generated with macro or written by hand or delegated again to Enumerable.Map.member? 也可以使用宏生成或手动编写,或再次委托给Enumerable.Map.member? to remove runtime penalty of Map.from_struct . 删除Map.from_struct运行时损失。

All the code above is generic so it can be placed to use -able module like 上面的所有代码都是通用的,因此可以放置在可use模块中,例如


defmodule StructEnumerable do
  defmacro __using__(opts \\ []) do
    quote do
      defimpl Enumerable do
        alias Enumerable, as: E
        def count(c), do: Map.from_struct(c) |> E.count()
        def member?(c, m), do: Map.from_struct(c) |> E.member?(m)
        def reduce(c, a, f), do: Map.from_struct(c) |> E.reduce(a, f)
      end

      # you can also add following code
      # to provide Fetch behaviour implementation
      # for your structs
      defdelegate([
        fetch(t, k),
        get_and_update(t, k, l)
      ], to: Map)
    end
  end
end

and embed it like 并像嵌入


  defmodule Contact do
    defstruct [:name, group: "", version: "", status: :latent]
    use StructEnumerable
  end

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM