27

也许有人可以帮助我。

从 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 行中定义的任何其他字段?处理更多的列和行。

非常感激!

4

6 回答 6

34

像这样(它也适用于其他 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 }
于 2011-12-12T15:58:30.247 回答
6
CSV.read(file_path, headers:true, header_converters: :symbol, converters: :all).collect do |row|
  Hash[row.collect { |c,r| [c,r] }]
end
于 2013-06-30T04:57:59.693 回答
1

要添加到迈克尔科尔的答案,如果您想以下列方式访问元素

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
于 2015-03-16T10:08:18.377 回答
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指令处理了这一点——而且整个事情都是即时的。这比上次好,因为现在我可以从 Ruby 访问所有字段,并且关联地,不再关心列号,就像awk.

于 2012-03-24T10:52:30.627 回答
0

不像 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
于 2013-08-02T01:45:45.010 回答
-1

虽然这不是原始问题的 100% 原生 Ruby 解决方案,但如果其他人在这里绊倒并想知道我现在使用什么 awk 调用,这里是:

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

其中 $stock 是我之前分配给公司股票代码的变量(wannabe 关键字段)。如果出现以下情况,可以通过返回 0.0 来方便地解决问题:股票代码或文件或字段 #9 未找到/为空,或者如果值无法转换为浮点数。因此,在我的情况下,任何尾随的 '%' 都会被很好地截断。

请注意,此时可以轻松地在 awk 中添加更多过滤器,以使 IO.readlines 从较小的结果 CSV 返回一个 1-dim 输出行数组,例如。

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

在 bash 中输出哪些行的 DivYld(第 9 列)超过 2.01,价格(第 2 列)超过 99.99。(不幸的是,我没有使用标题行来确定字段编号,这是我最终希望找到一些可搜索的关联 Ruby 数组的地方。)

于 2011-12-14T18:10:44.547 回答