0
votes

I am new to Elixir and functional programming in general. What I want is to update a value for a specific key in a map and then merge that map with another.

Here is my initial map:

%{      
  "_id" => "exampleaaaaaaaaaaaaaaaaaaaaaaaaa",
  "event" => "click",
  "ip" => "127.0.0.1",
  "location" => %{
    "city" => "Oklahoma City",
    "country" => "United States",
    "country_short" => "US",
    "latitude" => 35.4675598145,
    "longitude" => -97.5164337158,
    "postal_code" => "73101",
    "region" => "Oklahoma",
    "timezone" => "-05:00"
  },
  "msg" => %{
    "_id" => "exampleaaaaaaaaaaaaaaaaaaaaaaaaa",
    "_version" => "exampleaaaaaaaaaaaaaaa",
    "clicks" => [%{"ts" => 1365111111, "url" => "http://mandrill.com"}],
    "email" => "[email protected]",
    "metadata" => %{"user_id" => 111},
    "opens" => [%{"ts" => 1365111111}],
    "sender" => "[email protected]",
    "state" => "sent",
    "subject" => "This an example webhook message",
    "tags" => ["webhook-example"],
    "ts" => 1365109999
  },
  "ts" => 1519061856,
  "url" => "http://mandrill.com",
  "user_agent" => "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; en-US; rv:1.9.1.8) Gecko/20100317 Postbox/1.1.3",
  "user_agent_parsed" => %{
    "mobile" => false,
    "os_company" => "Apple Computer, Inc.",
    "os_company_url" => "http://www.apple.com/",
    "os_family" => "OS X",
    "os_icon" => "http://cdn.mandrill.com/img/email-client-icons/macosx.png",
    "os_name" => "OS X 10.6 Snow Leopard",
    "os_url" => "http://www.apple.com/osx/",
    "type" => "Email Client",
    "ua_company" => "Postbox, Inc.",
    "ua_company_url" => "http://www.postbox-inc.com/",
    "ua_family" => "Postbox",
    "ua_icon" => "http://cdn.mandrill.com/img/email-client-icons/postbox.png",
    "ua_name" => "Postbox 1.1.3",
    "ua_url" => "http://www.postbox-inc.com/",
    "ua_version" => "1.1.3"
  }
}

I have this code that's working fine:

merge_maps = get_in_attempt(
  payload, ["msg", "clicks"]
) ++ get_in_attempt(
  payload, ["msg", "opens"]
)
main_map = %{
  "ip" => get_in(payload, ["ip"]),
  "city" => get_in(payload, ["location", "city"]),
  "user_agent" => get_in(payload, ["user_agent"]),
  "event_type" => get_in(payload, ["event"])
}

new_maps = Enum.map(merge_maps, fn elem ->
  Map.update!(elem, "ts", &DateTime.from_unix!/1)
end)
|> Enum.map(fn elem ->
  Map.merge(elem, main_map)
end)

The output of new_maps:

[       
  %{
    "city" => "Oklahoma City",
    "event_type" => "click",
    "ip" => "127.0.0.1",
    "ts" => #DateTime<2013-04-04 21:31:51Z>,
    "url" => "http://mandrill.com",
    "user_agent" => "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; en-US; rv:1.9.1.8) Gecko/20100317 Postbox/1.1.3"
  },
  %{
    "city" => "Oklahoma City",
    "event_type" => "click",
    "ip" => "127.0.0.1",
    "ts" => #DateTime<2013-04-04 21:31:51Z>,
    "user_agent" => "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; en-US; rv:1.9.1.8) Gecko/20100317 Postbox/1.1.3"
  }
]

I feel like it's an overkill to enumerate twice over the list of maps but maybe that's a "good" way to achieve it considering the immutability aspect of functional programming.

Is this "best practice"?

1
What's the expected output? - Dogbert
oops. Yup here it is, check the edit :) - Cyzanfar

1 Answers

2
votes

I feel like it's an overkill to enumerate twice over the list of maps

You can merge the two Enum.map/2 calls like this:

new_maps = Enum.map(merge_maps, fn elem ->
  elem
  |> Map.update!("ts", &DateTime.from_unix!/1)
  |> Map.merge(main_map)
end)

Other than that, the code looks good to me.