【问题标题】:How to parse a Hash of Hashes from a CSV file如何从 CSV 文件中解析哈希值
【发布时间】:2017-05-29 13:27:50
【问题描述】:

我有一个 CSV 文件,我需要读取并提取在一定范围内具有“created_at”的所有行。 CSV 本身在 Excel 中大约有 5000 行。

这就是我从文件中提取信息的方式:

CSV.foreach("sample_data.csv", :headers => true, :header_converters => :symbol, :converters => :all) do |row|
  data[row.fields[0]] = Hash[row.headers[1..-1].zip(row.fields[1..-1])]
end

这是使用CSV.foreach后创建的最后一个Hash:

2760=>{:created_at=>1483189568, :readable_date=>"12/31/2016", :first_name=>"Louise", :last_name=>"Garza", :email=>"lgarza24n@drupal.org", :gender=>"Female", :company=>"Cogilith", :currency=>"EUR", :word=>"orchestration", :drug_brand=>"EPIVIR", :drug_name=>"lamivudine", :drug_company=>"State of Florida DOH Central Pharmacy", :pill_color=>"Maroon", :frequency=>"Yearly", :token=>"_", :keywords=>"in faucibus", :bitcoin_address=>"19jTjXLPQUL1nEmHrpqeqM1FdtDFZmUZ2E"}}

当我运行data[2759].first 时,我得到:

created_at
1309380645

我需要提取created_atrange = 1403321503..1406082945 之间的每个哈希值。我在data 哈希上使用eachcollect 尝试了大约20 种不同的方法,但没有成功。我的最后一次尝试为每个原始哈希打印了一个空的{}

我正在尝试对此进行测试,但没有成功:

data.each do |hash|
  if hash.first.to_s.to_i > 1403321503 && hash.first.to_s.to_i < 1406082945
    puts hash
  end
end

我不确定如何隔离key:created_at 的值,然后查看它是否在范围内。我也尝试过hash.first.to_s.to_i =/== range。

我可以通过使用 data[1].first.last 获得 :created_at 值,但是当我尝试在方法中使用它时会出错。

这是原始 CSV 的链接:goo.gl/NOjAPo

它不在我的工作计算机上,所以我无法对其进行粘贴。

【问题讨论】:

  • 从您的描述中无法判断发生了什么。您需要提供几行数据、产生错误答案的可运行代码以及相应的正确答案。例如。 data 是散列还是数组还不清楚。你为什么不直接用符号索引散列,hash[:created_at]
  • 提示:(x..y).include?(z) 是一种更简洁的检查某物是否在给定范围内的方法。你这里的内容要冗长得多,并且需要一堆冗余的方法调用。
  • 在使用 Ruby 时要记住的另一件事是尝试将您的问题分解为一系列链式但简单的操作。例如,reject 你不想要的行,或select 你想要的行,然后 puts 他们。

标签: ruby csv hash


【解决方案1】:

我只会在data 散列中存储范围内的行。性能更好的 IMO,因为它比将所有数据读入 data 并在第二步中删除不需要的条目需要更少的内存。

DATE_RANGE = (1403321503..1406082945)

CSV.foreach("sample_data.csv", 
            :headers => true, 
            :header_converters => :symbol, 
            :converters => :all) do |row|
  attrs = Hash[row.headers[1..-1].zip(row.fields[1..-1])]
  data[row.fields[0]] = attrs if DATE_RANGE.cover?(attrs[:created_at])
end

在实际创建哈希之前检查条件可能是有意义的,方法是检查 DATE_RANGE.cover? 与列号(row.fields[1] 中的 created_at 吗?)。

【讨论】:

  • 请尊重屏幕不太宽的人,在答案中拆分长行。
  • @mudasobwa 我认为没关系,因为该行是由 OP 提供的,没有引起问题,并且在我的回答中没有改变。对于那个很抱歉。我用改进的行长更新了我的答案。
  • 谢谢!提醒那些只听的人是有道理的:)
  • 这看起来很棒。我不确定是否可以在放入哈希之前直接尝试解析 CSV(不使用正则表达式或其他东西)。当我回到我的电脑时,我会检查一下!
  • 这正是我想要的。您帖子中的一个错字是 .covers?应该是.cover?非常感谢!
【解决方案2】:

使用Enumerable#select

hash.select do |_, v|
  (1403321503..1406082945) === v[:created_at]
end

这里我们还使用Range#===(也称为大小写相等或三重相等)来检查值是否在范围内。

【讨论】:

  • 当我在出错之前尝试做类似的事情时。我确实让它与 .to_s.to_i 一起使用。我应该在括号内还是外部添加 .to_s.to_i ? v[:created_at].to_s.to_i 或 v[:created_at.to_s.to_i]
  • 将符号转换为字符串然后再转换为整数没有多大意义。目前还不清楚,为什么要这样做,但如果你愿意,可以转换整个 v[:created_at] 值。
  • 当我尝试将 = 或 == 与范围一起使用时,我遇到了与类型进行比较的错误,单独尝试了 .to_i 但这不起作用所以我做了 -> 字符串 - > 整数以确保它是一个整数。
猜你喜欢
  • 2016-04-20
  • 2017-05-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-07-04
  • 2017-02-19
  • 2013-03-06
  • 1970-01-01
相关资源
最近更新 更多