减少一张地图中的地图列表

时间:2019-07-12 17:50:58

标签: elixir list-comprehension

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

[
  %{"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

5 个答案:

答案 0 :(得分:2)

使用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"}

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

NB 我没有测试此代码,但应该可以立即使用。

请注意,与String.split/2相比,该方法还有另一个优势-它可以处理无限流。

答案 1 :(得分:1)

您可以直接这样做:

message = "\x02\x1d0000 0000 \x1dAM01\x1cC41111\x1c\x1c\x1cC51\x1eAM04\x1cC22222\x1cC62\x1e\x03"

for segment <- String.split(message, ["\x02", "\x1d", "\x1e", "\x03"], trim: true), into: %{} do
  [head|tail] = String.split(segment, "\x1c", trim: true)
  {
    String.to_atom(head), 
    Map.new(tail, &String.split_at(&1, 2))
  }
end

输出:

%{
  "0000 0000 ": %{},
  AM01: %{"C4" => "1111", "C5" => "1"},
  AM04: %{"C2" => "2222", "C6" => "2"}
}

答案 2 :(得分:1)

  

在理想情况下,消息的最终结果将是:

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

您在这里:

message = "\x02\x1d0000 0000 \x1dAM01\x1cC41111\x1c\x1c\x1cC51\x1eAM04\x1cC22222\x1cC62\x1e\x03"

[header|segments] = String.split(message, ["\x02", "\x1d", "\x1e", "\x03"], trim: true)

for segment <- segments, into: %{HEADER: header} do
  [head|tail] = String.split(segment, "\x1c", trim: true)
  {
    String.to_atom(head), 
    Map.new(tail, &String.split_at(&1, 2))
  }
end

输出:

%{
  AM01: %{"C4" => "1111", "C5" => "1"},
  AM04: %{"C2" => "2222", "C6" => "2"},
  HEADER: "0000 0000 "
}

顺便说一下,Map.new()位是技巧。

答案 3 :(得分:0)

for map <- maps, into: %{} do
  [key] = Map.keys(map)
  {key, map[key]}
end

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

答案 4 :(得分:0)

  

使用String.split/2执行这样的任务非常困难   无效,轻率和无礼的方法。

Elixir的String.split()叫Erlang的binary:split()

-module(my).
-compile([export_all]).

go() ->
    Input = <<"\x1cHHheader\x1cAAaa segment\x1cBBbbsegment">>,
    binary:split(Input, <<"\x1c">>, [global, trim_all]).

在外壳中:

7> c(my).  
my.erl:2: Warning: export_all flag enabled - all functions will be exported
{ok,my}

8> my:go().
[<<"HHheader">>,<<"AAaa segment">>,<<"BBbbsegment">>]

而且,我认为binary:split()不仅有效,而且简短的一个内衬比相当混乱的多子句函数定义更优雅,并且简短的一个内衬更易于维护,并且绝对是不错的erlang