我有一个这样的地图列表:
[
%{"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
答案 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