Elixir:尝试将地图写入CSV,写成流结果

时间:2017-03-30 01:32:36

标签: csv stream elixir

我已经在这个问题上抓了一会儿。我正在尝试编写一个程序,将给定文本文件中每个单词的频率输出到.csv文件。我已经成功创建了查找每个单词频率的函数,并将其结果作为映射输出,但我的tocsv函数由于某种原因将结果写为Stream结果,我无法弄清楚为什么,或者如何避免这种情况。这是我的代码:

defmodule WordFrequency do

  def wordCount(readFile) do
     readFile
     |> words
     |> count
     |> tocsv
  end

  defp words(file) do
    file
    |> File.stream!
    |> Stream.map(&String.trim_trailing(&1))
    |> Stream.map(&String.split(&1,~r{[^A-Za-z0-9_]}))
    |> Enum.to_list
    |> List.flatten

  end

  defp count(words) when is_list(words) do
    Enum.reduce(words, %{}, &update_count/2)
  end

  defp update_count(word, acc) do
    Map.update acc, String.to_atom(word), 1, &(&1 + 1)
  end

  defp tocsv(map) do
    file = File.open!("test.csv", [:write, :utf8])
    map
    |> IO.inspect
    |> Enum.map(&CSV.encode(&1))
    |> Enum.each(&IO.inspect(file, &1, []))
  end

end

计数结果(它是一个测试文件)是:

bitterness: 1, fan: 1, respiration: 1, radiator: 1, ceiling: 1, run: 1,
  duck: 1, roundess: 1, terrorism: 1, she: 1, over: 1, equipment: 2, test: 1,
  freshness: 1, feminism: 1, bucket: 1, goodness: 1, manliness: 1,
  reflection: 1, uncomfortable: 1, tourism: 1, house: 1, ableism: 1, stairs: 1,
  heroism: 1, sadness: 1, socialism: 1, fruit: 1, dogs: 1, mechanism: 1,
  symbolism: 1, predilection: 1, up: 1, sedition: 1, faithfulness: 1,
  fruition: 1, criticism: 1, conformation: 1, extradition: 1, braveness: 1,
  ionization: 1, indigestion: 1, bubble: 1, introspection: 1, liquid: 1,
  apartment: 1, deep: 1, department: 1, centralization: 1, bitter: 1, ...}

所以我知道我没有将流传递给我的tocsv函数,但是在tocsv中发生的事情会将其转换为流并且在输出到csv文件之前不会将其转换为可写格式。任何人都知道如何解决这个问题?我正在使用此CSV模块:https://github.com/beatrichartz/csv

谢谢!

1 个答案:

答案 0 :(得分:5)

有一个示例是在您使用的README模块的CSV中生成CSV:

file = File.open!("test.csv", [:write, :utf8])
table_data |> CSV.encode |> Enum.each(&IO.write(file, &1))

请注意,IO.write/2将字节写入设备,而IO.inspect/3 使用IO设备根据给定选项检查第二个参数。另外,CSV.encode/1需要二维列表

那就是说,您可能应该坚持使用示例中提到的IO.write/2,并在count中生成一个二维列表,而不是Map

defp count(words) when is_list(words) do
  words
  |> Enum.reduce(%{}, &update_count/2)
  |> Enum.reduce([], fn {k, v}, acc -> [[k, v] | acc] end)
end

defp tocsv(map) do
  file = File.open!("test.csv", [:write, :utf8])

  map
  |> IO.inspect
  |> CSV.encode
  |> Enum.each(&IO.write(file, &1))
end

在这么简单的情况下,我只会使用裸 Elixir 生成一个文件,但是(假设count返回一个地图,就像在原始代码中一样):

defp tocsv(map) do
  File.open("test.csv", [:write, :utf8], fn(file) ->
    Enum.each(map, &IO.write(file, Enum.join(Tuple.to_list(&1), ?,) <> "\n"))
  end)
end

或者,甚至更简单:

defp tocsv(map) do
  File.write!("test.csv", 
     map
     |> Enum.map(Enum.join(Tuple.to_list(&1), ?,))
     |> Enum.join("\n"))
end