使用唯一键长生不老药

时间:2019-05-29 20:37:56

标签: elixir

我有这个json

[
  %{
    "159.69.136.31" => [
      %{"2015" => ["4"]},
      %{"2016" => []},
      %{"2017" => ["9"]},
      %{"2018" => []},
      %{"2019" => ["05"]}
    ]
  },
  %{ 
    "94.130.139.38" => [
      %{"2015" => []},
      %{"2016" => []},
      %{"2017" => []},
      %{"2018" => ["5", "8"]},
      %{"2019" => []}
    ]
  },
  %{
    "94.130.217.56" => [
      %{"2015" => []},
      %{"2016" => []},
      %{"2017" => []},
      %{"2018" => []},
      %{"2019" => []}
    ]
  }
]

我想做类似

的东西
[
  %{"2015" => ["4"]},
  %{"2016" => []},
  %{"2017" => ["9"]},
  %{"2018" => ["5", "8"]},
  %{"2019" => ["05"]}
]

基本上,它合并了同一年的密钥和地图上可用的数据。我用不同的方法尝试了此解决方案,但没有成功Elixir: Merge list with same map keys to one map

更新:年和IP不变 更新有关此的更多信息。

years = ["2015", "2016", "2017", "2018", "2019"]
servers = [@seaweedfs_new, @seaweedfs_old, @seaweedfs_oldest]

  Enum.map(servers, fn server ->
    type = seaweefs_type(server)
    attribute = seaweedfs_attribute(server)
    url = "http://" <> server <> ":8888" <> "/#{camera.exid}/snapshots/recordings/"
    year_values =
      Enum.map(years, fn year ->
        final_url = url <> year <> "/"
        %{
          "#{year}" => request_from_seaweedfs(final_url, type, attribute)
        }
      end)
    %{
      "#{server}" => year_values
    }
  end)

这是我获取年份值并将其作为服务器对象的方式。是否有可能在甚至获得年份值的情况下将其分解?

"#{year}" => request_from_seaweedfs(final_url, type, attribute)该请求基本上返回诸如"2015" => ["1", "2", "3"]之类的信息,是否有可能在进入服务器之前先合并几年?

3 个答案:

答案 0 :(得分:2)

您需要进入平面地图,然后将所有地图合并。

input
|> Enum.flat_map(&Map.values/1)
|> Enum.flat_map(& &1)
|> Enum.reduce(&Map.merge(&1, &2, fn _, v1, v2 ->
  v1 ++ v2
end))
#⇒ %{
#    "2015" => ["4"],
#    "2016" => [],
#    "2017" => ["9"],
#    "2018" => ["5", "8"],
#    "2019" => ["05"]
# }

要获取地图列表(恕我直言,这是错误的设计决定),只需执行以下操作:

for {k, v} <- map, do: %{k => v}
#⇒ [
#    %{"2015" => ["4"]},
#    %{"2016" => []},
#    %{"2017" => ["9"]},
#    %{"2018" => ["5", "8"]},
#    %{"2019" => ["05"]}
# ]

答案 1 :(得分:1)

要回答您的第一个问题,即如何用X制作Y,这是一个快速解决方案:

data = [
  %{
    "159.69.136.31" => [
      %{"2015" => ["4"]},
      %{"2016" => []},
      %{"2017" => ["9"]},
      %{"2018" => []},
      %{"2019" => ["05"]}
    ]
  },
  %{
    "94.130.139.38" => [
      %{"2015" => []},
      %{"2016" => []},
      %{"2017" => []},
      %{"2018" => ["5", "8"]},
      %{"2019" => []}
    ]
  },
  %{
    "94.130.217.56" => [
      %{"2015" => []},
      %{"2016" => []},
      %{"2017" => []},
      %{"2018" => []},
      %{"2019" => []}
    ]
  }
]

代码:

data
|> Enum.map(&Map.values/1)
|> List.flatten
|> Enum.map(fn year_map -> year_map |> Map.to_list |> hd end)
|> Enum.reduce(%{}, fn {year, year_data}, acc ->
  Map.update(acc, year, year_data, &[&1 | year_data])
end)
|> Enum.map(fn {year, year_data} ->
  {year, List.flatten(year_data) |> Enum.reject(&is_nil/1)}
end)
|> IO.inspect

返回:

[
  {"2015", ["4"]},
  {"2016", []},
  {"2017", ["9"]},
  {"2018", ["5", "8"]},
  {"2019", ["05"]}
]

在管道和函数中插入更多IO.inspect,以查看正在进行的转换。

答案 2 :(得分:0)

你为什么这样做:

"#{server}" => year_values

当您不在乎最终结果中的服务器名称时?这样做会使访问相关数据变得更加困难。

您可以结合使用for loop选项(elixir 1.8+)和:reduce(也称为列表理解)来创建结果图:

defmodule My do

  @years ["2015", "2016", "2017", "2018", "2019"]
  @servers ["new", "old", "oldest"] 

  def request_from_seaweedfs(url, _type, _attribute) do
    IO.puts url
    "#{:random.uniform(20)}"  #Returns a random string
  end

  def get_data do
    for server <- @servers, year <- @years, reduce: %{} do
      map -> #This is the map specified here --------^
        url = "http://#{server}:8888/camera.exid/snapshots/recordings/#{year}/"
        result = request_from_seaweedfs(url, "x", "y")
        IO.puts "#{year}: #{result}"
        Map.update(map, year, [result], &([result|&1]))
    end
  end

end

输出:

~/elixir_programs$ iex my.ex
Erlang/OTP 20 [erts-9.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]
Interactive Elixir (1.8.2) - press Ctrl+C to exit (type h() ENTER for help)

iex(1)> My.get_data
http://new:8888/camera.exid/snapshots/recordings/2015/
2015: 9
http://new:8888/camera.exid/snapshots/recordings/2016/
2016: 15
http://new:8888/camera.exid/snapshots/recordings/2017/
2017: 19
http://new:8888/camera.exid/snapshots/recordings/2018/
2018: 11
http://new:8888/camera.exid/snapshots/recordings/2019/
2019: 7
http://old:8888/camera.exid/snapshots/recordings/2015/
2015: 12
http://old:8888/camera.exid/snapshots/recordings/2016/
2016: 19
http://old:8888/camera.exid/snapshots/recordings/2017/
2017: 14
http://old:8888/camera.exid/snapshots/recordings/2018/
2018: 10
http://old:8888/camera.exid/snapshots/recordings/2019/
2019: 12
http://oldest:8888/camera.exid/snapshots/recordings/2015/
2015: 3
http://oldest:8888/camera.exid/snapshots/recordings/2016/
2016: 5
http://oldest:8888/camera.exid/snapshots/recordings/2017/
2017: 14
http://oldest:8888/camera.exid/snapshots/recordings/2018/
2018: 4
http://oldest:8888/camera.exid/snapshots/recordings/2019/
2019: 12

%{
  "2015" => ["3", "12", "9"],
  "2016" => ["5", "19", "15"],
  "2017" => ["14", "14", "19"],
  "2018" => ["4", "10", "11"],
  "2019" => ["12", "12", "7"]
}

要获取列表,您可以执行以下操作:

My.get_data |> Enum.map(fn {key,val} -> %{key => val} end)

产生:

[
  %{"2015" => ["3", "12", "9"]},
  %{"2016" => ["5", "19", "15"]},
  %{"2017" => ["14", "14", "19"]},
  %{"2018" => ["4", "10", "11"]},
  %{"2019" => ["12", "12", "7"]}
]

而且,如果需要维护数据的顺序,则可以执行以下操作:

My.get_data |> Enum.map(fn {key,val} -> %{key => Enum.reverse(val)} end)