【问题标题】:How to Modify a Map in Elixir如何在 Elixir 中修改地图
【发布时间】:2018-02-10 01:26:42
【问题描述】:

我使用 ElixirPhoenix 创建了一个 JSON api

我的控制器中有一个用于创建操作的端点,它采用如下所示的 json 数据:

     [{"opens_detail"=>
          [{"ua"=>"Linux/Ubuntu/Chrome/Chrome 28.0.1500.53",
              "ip"=>"55.55.55.55",
              "ts"=>1365190001,
              "location"=>"Georgia, US"}],
         "template"=>"example-template",
         "metadata"=>{"user_id"=>"123", "website"=>"www.example.com"},
         "clicks"=>42,
         "ts"=>1365190000,
         "state"=>"sent",
         "clicks_detail"=>
          [{"ua"=>"Linux/Ubuntu/Chrome/Chrome 28.0.1500.53",
              "ip"=>"55.55.55.55",
              "ts"=>1365190001,
              "url"=>"http://www.example.com",
              "location"=>"Georgia, US"}],
         "email"=>"recipient.email@example.com",
         "subject"=>"example subject",
         "sender"=>"sender@example.com",
         "_id"=>"abc123abc123abc123abc123",
         "tags"=>["password-reset"],
         "opens"=>42}]

我的目标是获取这个 json 并从中创建一个新的,其中一些键和值被重命名以匹配我下面的架构:

web/models/messages.ex

   ...
      schema "messages" do
        field :sender, :string
        field :uniq_id, :string # equal to '_id' in the payload
        field :ts, :utc_datetime
        field :template, :string
        field :subject, :string
        field :email, :string
        field :tags, {:array, :string}
        field :opens, :integer
        field :opens_ip, :string # equal to nested 'ip' value in 'open_details'
        field :opens_location, :string # equal to nested 'location' value in 'open_details'
        field :clicks, :integer
        field :clicks_ip, :string # equal to nested 'ip' value in 'click_details'
        field :clicks_location, :string # equal to nested 'location' value in 'click_details'
        field :status, :string # equal to the "state" in the payload

        timestamps()
      end
  ...

这是我尝试过的:

web/controller/message_controller.ex 中:

  def create(conn, payload) do

    %{ payload |
      "uniq_id" => payload["_id"],
      "status" => payload["type"]
      "open_ips" =>  Enum.at(payload["opens_detail"], 0)['ip'],
      "open_location" => Enum.at(payload["opens_detail"], 0)['location'],
      "click_ips" =>  Enum.at(payload["clicks_detail"], 0)['ip'],
      "click_location" => Enum.at(payload["clicks_detail"], 0)['location'],
    }

    changeset = Message.changeset(%Message{}, payload)

   ...

  end

但很快就发现它也不起作用,因为我仍然需要删除一些键。

我来自 Ruby/Python (Rails/Django),不想开始用我的 OO 知识污染我对函数式编程的学习,特别是 elixir/phoenix。

你会如何解决这个问题?

【问题讨论】:

    标签: json elixir phoenix-framework elixir-poison


    【解决方案1】:

    你会如何解决这个问题?

    我会从头开始创建新地图,而不是更新原始地图。您可以使用get_in 来简化访问嵌套字段的逻辑。这是一个例子:

    map = %{
      uniq_id:        get_in(payload, ["_id"]),
      open_ips:       get_in(payload, ["opens_detail", Access.at(0), "ip"]),
      open_locations: get_in(payload, ["opens_detail", Access.at(0), "location"]),
    }
    

    如果您想从原始地图中选择一部分字段,可以使用Map.mergeMap.take

    Map.merge(Map.take(payload, [:sender, ...]), %{uniq_id: ...})
    

    但如果只是几个字段,我宁愿手动写出来。

    【讨论】:

    • 非常好,非常感谢。至于剩余的键/值,我需要像这样在地图中重新定义它们:%{ ..., sender: get_in(payload, ["sender"]), ...}?
    • 是的,我会那样做。您也可以像这样从原始地图中选择一些键:Map.merge(Map.take(payload, [:sender, ...]), %{uniq_id: ...}) 但如果只是几个字段,我宁愿手动将它们写出来。
    猜你喜欢
    • 2014-12-24
    • 1970-01-01
    • 2014-06-09
    • 1970-01-01
    • 1970-01-01
    • 2018-12-27
    • 1970-01-01
    • 2016-09-24
    • 2022-10-12
    相关资源
    最近更新 更多