【问题标题】:Ruby - Array of Hashes to CSVRuby - 到 CSV 的哈希数组
【发布时间】:2017-11-30 04:53:06
【问题描述】:

我收集了一些数据并将其保存到哈希数组中,格式如下:

info = [
  {'Name' => 'Alex', 'Tel' => 999, 'E-mail' => "bla@bla.com"},
  {'Name' => 'Ali', 'Tel' => 995, 'E-mail' => "ali@bla.com"}
  # ...
]

但并非所有信息都始终存在:

{'Name' => 'GuyWithNoTelephone', 'E-mail' => "poor@bla.com"}

我想将此信息转换为 CSV 文件。这是我尝试过的:

def to_csv (info)
  CSV.open("sm-data.csv", "wb") do |csv|
  csv << ["Name", "Tel", "E-mail"]
    info.each do |person|
      csv << person.values

当我尝试这样做时,表格的格式不正确,并且说,如果缺少电话号码,则该人的电子邮件会出现在电话栏中。

如何有选择地将此信息写入 CSV 文件,即如果缺少值,我如何告知跳过列?

【问题讨论】:

    标签: arrays ruby csv hash


    【解决方案1】:

    但遗憾的是,当我尝试这个时,表格的格式不正确,并且说,如果缺少电话号码,则该人的电子邮件会出现在电话栏中。

    这是因为您在这种情况下省略了电话号码,只提供了 3 个值中的 2 个。要解决此问题,您只需提供所有 3 个值,即使它们不存在于哈希中:

    csv << ['Name', 'Tel', 'E-mail']
    info.each do |person|
      csv << [person['Name'], person['Tel'], person['E-Mail']]
    end
    

    或通过Hash#values_at:

    csv << ['Name', 'Tel', 'E-mail']
    info.each do |person|
      csv << person.values_at('Name', 'Tel', 'E-Mail')
    end
    

    为:

    {'Name' => 'GuyWithNoTelephone', 'E-mail' => "poor@bla.com"}
    

    这会导致:

    csv << ['GuyWithNoTelephone', nil, 'poor@bla.com']
    

    生成此输出:(注意两个逗号,表示中间的空字段)

    "GuyWithNoTelephone,,poor@bla.com\n"
    

    【讨论】:

    • 谢谢,非常简单直接和快速的回​​答!
    【解决方案2】:

    试试这个,

      def to_csv(csv_filename="sm-data.csv")
        # Get all unique keys into an array:
        keys = info.map(&:keys).inject(&:|)
        CSV.open(csv_filename, "wb") do |csv|
          csv << keys
          info.each do |hash|
            # fetch values at keys location, inserting null if not found.
            csv << hash.values_at(*keys)
          end
        end
      end
    

    【讨论】:

    • @mudasobwa 将所有键放入一个巨大的数组并调用 uniq 应该返回所有键的联合。
    • 我认为info.map(&amp;:keys).inject(&amp;:|) 是一种优雅的方式......对于更文明的时代;-)
    • 编辑了答案@Stefan
    • 原始代码是info.flat_map(&amp;:keys).uniq(IMO 比info.map(&amp;:keys).inject(&amp;:|) 更容易阅读)。 @AlekseiMatiushkin,flat_map 是如何冗余的? uniq 有什么问题?
    【解决方案3】:

    就这么简单:

    path = "data/backtest-results.csv"
    CSV.open(path, "wb") do |csv|
      csv << ["Asset", "Strategy", "Profit"]
      result_array.each do |p|
        csv << p.map { |key, value| value }
      end
    end
    

    如果您想在新数据到来时继续添加,请使用 "(File.file?(path) ? "ab" : "wb")" 而不是 "wb"

    【讨论】:

      猜你喜欢
      • 2016-05-16
      • 2014-08-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-11
      • 2021-02-05
      • 2014-03-03
      相关资源
      最近更新 更多