You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Elixir将文件解析为Map:Enum.zip函数使用是否正确?

问题:解析文本到Elixir Map仅得到3个条目而非全部

尝试将外部查询结果的文本文件解析为Elixir的Map,实际数据有30余条,但最终仅得到3个条目。通过inspect查看确认所有数据都被解析过,但结果不符合预期,怀疑是否错误使用了Enum.zip函数。

模块代码

defmodule Statistics do
  
  def read(path) do
      case File.read(path) do
        {:ok, body} ->
           parse_body(body)
        {:error, reason} ->
          IO.puts(~s(could not open file "#{path}"\n))
          IO.puts(~s("#{:file.format_error(reason)}\n"))
      end
    end

  def parse_lines(lines, keys) do
    Enum.reduce(lines, %{}, fn line, built ->
      [name | fields] = String.split(line, "\t")

      # IO.inspect(fields)

      if Enum.count(keys) == Enum.count(fields) do
        line_data = Enum.zip(keys, fields) |> Enum.into(%{})
        Map.merge(built, %{name => line_data})
      else
        built
      end
    end)
  end

  def parse_body(body) do
    [header | lines] = String.split(body, ~r(\r\n|\r|\n))
    keys = tl(String.split(header, "\t"))
    parse_lines(lines, keys)
  end

end

文本文件内容

Abandon_agent    Abandon_system  answered_greater_than_20    answered_less_than_20   answered_less_than_15   answered_less_than_10   answered_less_than_5    date
0   0   0   0   0   0   0   2022-09-29
0   0   0   0   0   0   0   2022-10-01
0   0   0   0   0   0   0   2022-10-02
0   0   0   0   4   24  9   2022-10-03
0   0   2   0   6   22  23  2022-10-04
2   0   0   0   7   16  21  2022-10-05
1   0   1   0   8   12  35  2022-10-06
0   0   0   2   8   9   29  2022-10-07
0   0   0   0   0   0   0   2022-10-08
0   0   0   0   0   0   0   2022-10-09
0   0   2   3   3   18  12  2022-10-10
0   0   1   2   5   16  6   2022-10-11
0   0   0   2   6   24  19  2022-10-12
0   0   1   2   3   20  29  2022-10-13
1   0   1   2   2   11  10  2022-10-14
0   0   0   0   0   0   0   2022-10-15
0   0   0   0   0   0   0   2022-10-16
0   0   1   1   6   17  11  2022-10-17
0   0   1   1   4   16  11  2022-10-18
0   0   0   1   7   18  12  2022-10-19
0   0   1   1   6   21  9   2022-10-20
1   0   0   3   1   20  17  2022-10-21
0   0   0   0   0   0   0   2022-10-22
0   0   0   0   0   0   0   2022-10-23
0   0   1   1   9   37  15  2022-10-24
0   0   0   1   4   21  14  2022-10-25
1   0   1   2   0   21  12  2022-10-26
0   0   3   2   7   17  13  2022-10-27
0   0   1   0   1   9   27  2022-10-28
0   0   0   0   0   0   0   2022-10-29
0   0   0   0   0   0   0   2022-10-30
0   0   0   0   4   21  8   2022-10-31

实际输出结果

%{
  "0" => %{
    "Abandon_system" => "0",
    "answered_greater_than_20" => "0",
    "answered_less_than_10" => "21",
    "answered_less_than_15" => "4",
    "answered_less_than_20" => "0",
    "answered_less_than_5" => "8",
    "date" => "2022-10-31"
  },
  "1" => %{
    "Abandon_system" => "0",
    "answered_greater_than_20" => "1",
    "answered_less_than_10" => "21",
    "answered_less_than_15" => "0",
    "answered_less_than_20" => "2",
    "answered_less_than_5" => "12",
    "date" => "2022-10-26"
  },
  "2" => %{
    "Abandon_system" => "0",
    "answered_greater_than_20" => "0",
    "answered_less_than_10" => "16",
    "answered_less_than_15" => "7",
    "answered_less_than_20" => "0",
    "answered_less_than_5" => "21",
    "date" => "2022-10-05"
  }
}

问题原因

和Enum.zip无关,核心问题在于你用了文本第一列(Abandon_agent)的值作为Map的键,而该列的值只有0、1、2三种。每次执行Map.merge(built, %{name => line_data})时,相同键对应的旧数据会被新数据覆盖,最终每个键只保留最后一次出现的那条记录。

解决方案

要保留所有数据,需要更换唯一的键,或者将所有条目存储为列表:

方案1:用date作为唯一键(日期不重复)

修改parse_lines函数:

def parse_lines(lines, keys) do
  Enum.reduce(lines, %{}, fn line, built ->
    [_abandon_agent | fields] = String.split(line, "\t")
    date = List.last(fields) # 提取日期作为键
    if Enum.count(keys) == Enum.count(fields) do
      line_data = Enum.zip(keys, fields) |> Enum.into(%{})
      Map.merge(built, %{date => line_data})
    else
      built
    end
  end)
end

方案2:返回所有条目的列表

如果不需要Map结构,直接返回解析后的列表:

def parse_lines(lines, keys) do
  Enum.flat_map(lines, fn line ->
    [_abandon_agent | fields] = String.split(line, "\t")
    if Enum.count(keys) == Enum.count(fields) do
      [Enum.zip(keys, fields) |> Enum.into(%{})]
    else
      []
    end
  end)
end

方案3:使用复合键(比如Abandon_agent + date)

如果需要保留原列信息,用组合键避免重复:

def parse_lines(lines, keys) do
  Enum.reduce(lines, %{}, fn line, built ->
    [abandon_agent | fields] = String.split(line, "\t")
    date = List.last(fields)
    key = "#{abandon_agent}_#{date}" # 生成复合键
    if Enum.count(keys) == Enum.count(fields) do
      line_data = Enum.zip(keys, fields) |> Enum.into(%{})
      Map.merge(built, %{key => line_data})
    else
      built
    end
  end)
end

内容的提问来源于stack exchange,提问作者keepTrackOfYourStack

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 08:40:43