首页 > 解决方案 > 减少一张地图中的地图列表

问题描述

我有一个这样的地图列表:

[
  %{"000000000 000000000000 00000000 ": %{}},
  %{AM01: %{"C4" => "11111111", "C5" => "1"}},
  %{AM04: %{"C2" => "22222222", "C6" => "2"}}
]

如何在一张地图中减少此地图列表,如下所示?

%{
   "000000000 000000000000 00000000 ": %{},
    AM01: %{"C4" => "11111111", "C5" => "1"},
    AM04: %{"C2" => "22222222", "C6" => "2"}
}

生成这个地图列表的代码是这样的:

for segment <- Enum.filter(String.split(message, ["\x02", "\x1d", "\x1e", "\x03"]), fn x -> x != "" end) do
    [head | tail] = Enum.filter(String.split(segment, "\x1c"), fn x -> x != "" end)
    %{String.to_atom(head) => Map.new(tail, &String.split_at(&1, 2))}
end

标签: elixirlist-comprehension

解决方案


String.split/2用于这样的任务是一种极其低效、不优雅和非英语的方法。作为电信的孩子,Erlang(以及 Elixir)在解决这些任务方面特别出色。

它们都将通过递归解析数据、标记上的模式匹配来解决。

由于您没有发布真实输入的示例,因此我无法提出一个可行的示例,但方法应该是:

defmodule Parse do
  @input "\x1cHHheader\x1cAAaa segment\x1cBBbbsegment"

  def parse("", {{typ, txt}, map}), do: Map.put(map, typ, txt)
  def parse(<<"\x1c", type :: binary-size(2), rest :: binary>>, {{typ, txt}, map}),
    do: parse(rest, {{type, ""}, Map.put(map, typ, txt)})
  def parse(<<c :: binary-size(1), rest :: binary>>, {{typ, txt}, map}),
    do: parse(rest, {{typ, txt <> c}, map})

  def test(input \\ @input), do: parse(input, {{nil, ""}, %{}})
end

并像这样使用它:

Parse.test
#⇒ %{"AA" => "aa segment", "BB" => "bbsegment", "HH" => "header"}

当然,真正的代码会更复杂,你需要对许多不同的子句进行模式匹配,但我敢打赌这个想法很清楚。

注意我没有测试这段代码,但它应该可以开箱即用。

请注意,这种方法有另一个优势String.split/2——它能够处理无限流。


推荐阅读