将 CSV 文件转换为哈希数组
Posted
技术标签:
【中文标题】将 CSV 文件转换为哈希数组【英文标题】:Convert CSV file into array of hashes 【发布时间】:2012-12-21 09:48:26 【问题描述】:我有一个 csv 文件,一些曲棍球数据,例如:
09.09.2008,1,HC Vitkovice Steel,BK Mlada Boleslav,1:0 (PP)
09.09.2008,1,HC Lasselsberger Plzen,RI OKNA ZLIN,6:2
09.09.2008,1,HC Litvinov,HC Sparta Praha,3:5
我想将它们保存在哈希数组中。我没有任何标题,我想为每个值添加键,例如 "time" => "09.09.2008"
等等。每一行都应该可以通过arr[i]
来访问,每个值可以通过例如arr[i]["time"]
来访问。我更喜欢CSV
类而不是FasterCSV
或split
。你能指出方法或重定向到解决类似问题的某个线程吗?
【问题讨论】:
什么是so on
?除了"time"
,我们不知道你想要什么键。也许你应该显示预期的输出。
等等..我的意思是等等,我的英语可能不太好。其他关键可能是回合、主队和客队以及得分。预期输出为 ["time" => "09.09.2008", "round" => "1", "home" => "Vitkovice Steel", "visiting" => "BK Mlada Boleslav", "score" = > "1:0 (PP)"],[下一行...]
【参考方案1】:
只要通过headers: true
CSV.foreach(data_file, headers: true) do |row|
puts row.inspect # hash
end
从那里,您可以随意操作散列。
(使用 Ruby 2.0 测试,但我认为这已经工作了很长时间。)
编辑
您说您没有任何标题 - 您可以在阅读文件内容后在文件内容的开头添加标题行吗?
【讨论】:
从技术上讲,我认为每一行都是CSV::Row
的一个实例,它的作用类似于Hash
,但实际上并不继承Hash
。
你也可以使用CSV.parse(data, headers: true).map(&:to_h)
来达到类似的效果,考虑到上面 Jared 的注释。这会将您的 CSV 转换为以标题为键的哈希数组。您还可以加入选项header_converters: :symbol
以使用符号作为键而不是列名作为字符串。【参考方案2】:
您可以使用Ruby CSV parser 对其进行解析,然后使用Hash[ keys.zip(values) ]
使其成为哈希。
例子:
test = '''
09.09.2008,1,HC Vitkovice Steel,BK Mlada Boleslav,1:0 (PP)
09.09.2008,1,HC Lasselsberger Plzen,RI OKNA ZLIN,6:2
09.09.2008,1,HC Litvinov,HC Sparta Praha,3:5
'''.strip
keys = ['time', etc... ]
CSV.parse(test).map |a| Hash[ keys.zip(a) ]
【讨论】:
我试过 'require 'csv' csv_data = CSV.read 'data.txt' keys = ['time',"round","home","visiting","score"] CSV .parse(csv_data).map |a| Hash[ keys.zip(a) ] ' 但我可能没有抓住这个想法,因为它返回了一些错误 c:/Ruby193/lib/ruby/1.9.1/csv.rb:1381:inensure in parse': undefined method
close' for #<0x8f2050>
0x8f2050>
【参考方案3】:
这是 Josh Nichols 的 fantastic post,它解释了如何按照您的要求进行操作。
总结一下,这里是他的代码:
csv = CSV.new(body, :headers => true, :header_converters => :symbol, :converters => [:all, :blank_to_nil])
csv.to_a.map |row| row.to_hash
=> [:year=>1997, :make=>"Ford", :model=>"E350", :description=>"ac, abs, moon", :price=>3000.0, :year=>1999, :make=>"Chevy", :model=>"Venture \"Extended Edition\"", :description=>nil, :price=>4900.0, :year=>1999, :make=>"Chevy", :model=>"Venture \"Extended Edition, Very Large\"", :description=>nil, :price=>5000.0, :year=>1996, :make=>"Jeep", :model=>"Grand Cherokee", :description=>"MUST SELL!\nair, moon roof, loaded", :price=>4799.0]
因此,您可以将 CSV 文件的正文保存到名为 body
的字符串中。
body = "09.09.2008,1,HC Vitkovice Steel,BK Mlada Boleslav,1:0 (PP)
09.09.2008,1,HC Lasselsberger Plzen,RI OKNA ZLIN,6:2
09.09.2008,1,HC Litvinov,HC Sparta Praha,3:5"
然后运行他上面列出的代码就可以了。
【讨论】:
感谢帖子参考,很有用。我不确定这是否是我的数据;但是,我需要省略:blank_to_nil
否则我会收到错误 *** NoMethodError Exception: undefined method
arity' for nil:NilClass`
@CodeBiker @shakerlxxv 我有同样的错误信息,NoMethodError: undefined method encode
for nil:Nilclass。有什么办法让它工作?
@user2012677,我省略了:blank_to_nil
转换器,所以我的最终表达式看起来像CSV.new(File.new(file), :headers => true, :header_converters => :symbol, :converters => [:all]).to_a.map |row| row.to_hash
您也可以将其简化为:CSV.new(body, headers: true).map(&:to_hash)
(至少在 Ruby 2.1.5 中)
我更喜欢上面 Pavel 的解决方案【参考方案4】:
稍微短一点的解决方案
解析字符串:
CSV.parse(content, headers: :first_row).map(&:to_h)
解析文件:
CSV.open(filename, headers: :first_row).map(&:to_h)
【讨论】:
解析文件代码导致:/usr/lib/ruby/2.5.0/csv.rb:1764:in
to_h': Error element type String at 0 (expected array) (TypeError)`【参考方案5】:
Nathan Long 的回答略有不同
data_file = './sheet.csv'
data = []
CSV.foreach(data_file, headers: true) do |row|
data << row.to_hash
end
现在data
是一个哈希数组供您竞标!
【讨论】:
更精简:data = CSV.foreach(data_file, headers: true).map |row| row.to_h
【参考方案6】:
CSV 模块的 headers
选项接受一个字符串数组作为标题,当它们不作为 CSV 内容的第一行出现时。
CSV.parse(content, headers: %w(time number team_1 team_2 score))
这将使用给定的标头作为键生成可枚举的哈希。
【讨论】:
【参考方案7】:你也可以试试下面的 gem
require 'csv_hasher'
arr_of_hashes = CSVHasher.hashify('/path/to/csv/file')
返回的哈希键将是 csv 文件的标头值。
如果你想传递自己的密钥,那么
keys = [:key1, :key2, ... ]
arr_of_hashers = CSVHasher.hashify('/path/to/csv/file', keys: keys )
【讨论】:
以上是关于将 CSV 文件转换为哈希数组的主要内容,如果未能解决你的问题,请参考以下文章