从CSV导入到Ruby数组中,将第一个字段作为哈希键,然后查找给定标题行字段值

dced5bon  于 2022-11-04  发布在  Ruby
关注(0)|答案(7)|浏览(179)

也许有人能帮我。
从如下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]

如何将这个数组转换为一个哈希值,使用ticker字段作为唯一键,这样我就可以轻松地查找输入的第1行中定义的任何其他关联字段了?
非常感谢!

dfuffjeb

dfuffjeb1#

如下所示(它也适用于其他CSV,而不仅仅是您指定的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 }
k2arahey

k2arahey2#

CSV.read(file_path, headers:true, header_converters: :symbol, converters: :all).collect do |row|
  Hash[row.collect { |c,r| [c,r] }]
end
eblbsuwk

eblbsuwk3#

为了补充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
hec6srdp

hec6srdp4#

为了获得两个世界的最佳效果(非常快速地阅读一个巨大的文件和一个本地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文件的第一行。内联sed指令负责这一点--整个过程非常即时。这比last好,因为现在我可以访问Ruby中的所有字段,而不再像awk那样关心列号。

31moq8wy

31moq8wy5#

不像1-liner-ie,但这对我来说更清楚。

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
enxuqcxy

enxuqcxy6#

CSV.read(file_path, headers:true, header_converters: :symbol, converters: :all).collect do |row|
  row.to_h
end
vjhs03f7

vjhs03f77#

虽然这不是原始问题的100%原生Ruby解决方案,但如果其他人在这里遇到困难,想知道我现在使用的awk调用是什么,那么它就是:

$dividend_yield = IO.readlines("|awk -F, '$1==\"#{$stock}\" {print $9}' datafile.csv")[0].to_f

其中$stock是我先前分配给公司股票代码(想要成为关键字段)的变量。如果满足以下条件,则返回0.0,从而方便地解决问题:股票代码或文件或字段#9没有找到或为空,或者值不能转换为浮点型。因此,在我的情况下,任何尾随的“%”都会被很好地截断。
请注意,此时可以在awk中轻松添加更多过滤器以获得IO。readlines从较小的CSV结果中返回一个一维输出行数组,例如。

awk -F, '$9 >= 2.01  &&  $2 > 99.99  {print $0}' datafile.csv

在bash中输出哪些行的DivYld(第9列)大于2.01,price(第2列)大于99.99。(不幸的是,我没有使用标题行来确定字段编号,而这正是我最终希望的一些可搜索的关联Ruby数组。)

相关问题