也许有人可以帮助我。
从如下的CSV文件开始:
Ticker,"Price","Market Cap"
ZUMZ,30.00,933.90
XTEX,16.02,811.57
AAC,9.83,80.02
我设法把它们读成阵列:
require 'csv'
tickers = CSV.read("stocks.csv", {:headers => true, :return_headers => true, :header_converters => :symbol, :converters => :all} )
要验证数据,这可行:
puts tickers[1][:ticker]
ZUMZ
然而,这不是:
puts tickers[:ticker => "XTEX"][:price]
如何使用自动收录器字段作为唯一键将此数组转换为哈希值,以便我可以轻松地按照输入的第1行中的定义查找任何其他字段?处理更多的列和行。
非常感谢!
答案 0 :(得分:33)
像这样(它也适用于其他CSV,而不仅仅是你指定的那个):
require 'csv'
tickers = {}
CSV.foreach("stocks.csv", :headers => true, :header_converters => :symbol, :converters => :all) do |row|
tickers[row.fields[0]] = Hash[row.headers[1..-1].zip(row.fields[1..-1])]
end
结果:
{"ZUMZ"=>{:price=>30.0, :market_cap=>933.9}, "XTEX"=>{:price=>16.02, :market_cap=>811.57}, "AAC"=>{:price=>9.83, :market_cap=>80.02}}
您可以像这样访问此数据结构中的元素:
puts tickers["XTEX"][:price] #=> 16.02
编辑(根据评论):为了选择元素,您可以执行类似
的操作 tickers.select { |ticker, vals| vals[:price] > 10.0 }
答案 1 :(得分:5)
CSV.read(file_path, headers:true, header_converters: :symbol, converters: :all).collect do |row|
Hash[row.collect { |c,r| [c,r] }]
end
答案 2 :(得分:1)
如果您想以下列方式访问元素,请添加Michael Kohl的答案
puts tickers[:price]["XTEX"] #=> 16.02
您可以尝试以下代码段:
CSV.foreach("Workbook1.csv", :headers => true, :header_converters => :symbol, :converters => :all) do |row|
hash_row = row.headers[1..-1].zip( (Array.new(row.fields.length-1, row.fields[0]).zip(row.fields[1..-1])) ).to_h
hash_row.each{|key, value| tickers[key] ? tickers[key].merge!([value].to_h) : tickers[key] = [value].to_h}
end
答案 3 :(得分:0)
为了充分利用这两个方面(从一个巨大的文件中快速读取以及本机Ruby CSV对象的好处),我的代码已经演变成这种方法:
$stock="XTEX"
csv_data = CSV.parse IO.read(%`|sed -n "1p; /^#{$stock},/p" stocks.csv`), {:headers => true, :return_headers => false, :header_converters => :symbol, :converters => :all}
# Now the 1-row CSV object is ready for use, eg:
$company = csv_data[:company][0]
$volatility_month = csv_data[:volatility_month][0].to_f
$sector = csv_data[:sector][0]
$industry = csv_data[:industry][0]
$rsi14d = csv_data[:relative_strength_index_14][0].to_f
更接近我的原始方法,但只读取一个记录加上包含标题的输入csv文件的第1行。内联sed
指令可以解决这个问题 - 整个过程非常明显。这比last更好,因为现在我可以访问Ruby中的所有字段,并且可以关联,而不再像awk
那样关心列号。
答案 4 :(得分:0)
不像1-liner那样,但这对我来说更清楚。
csv_headers = CSV.parse(STDIN.gets)
csv = CSV.new(STDIN)
kick_list = []
csv.each_with_index do |row, i|
row_hash = {}
row.each_with_index do |field, j|
row_hash[csv_headers[0][j]] = field
end
kick_list << row_hash
end
答案 5 :(得分:-1)
虽然这不是原始问题的100%原生Ruby解决方案,但其他人是否应该偶然发现并且想知道我现在使用的是什么awk,这里是:
$dividend_yield = IO.readlines("|awk -F, '$1==\"#{$stock}\" {print $9}' datafile.csv")[0].to_f
其中$ stock是我之前分配给公司股票代码(崇拜关键字段)的变量。 通过返回0.0 if:ticker或文件或字段#9未找到/为空,或者如果值不能被类型化为浮点数,则可以方便地解决问题。因此,在我的情况下,任何尾随的'%'都会被很好地截断。
请注意,此时可以轻松地在awk中添加更多过滤器,以使IO.readlines从较小的结果CSV返回1-dim输出行数组,例如。
awk -F, '$9 >= 2.01 && $2 > 99.99 {print $0}' datafile.csv
以bash输出哪些行的DivYld(col 9)超过2.01且price(col 2)超过99.99。 (不幸的是我没有使用标题行来确定字段数,这是我最终希望获得一些可搜索的关联Ruby数组的地方。)