Ruby Autovivification

我一直试图在ruby中使用autovivification来对此进行简单的记录合并:

2009-08-21|09:30:01|A1|EGLE|Eagle Bulk Shpg|BUY|6000|5.03 2009-08-21|09:30:35|A2|JOYG|Joy Global Inc|BUY|4000|39.76 2009-08-21|09:30:35|A2|LEAP|Leap Wireless|BUY|2100|16.36 2009-08-21|09:30:36|A1|AINV|Apollo Inv Cp|BUY|2300|9.15 2009-08-21|09:30:36|A1|CTAS|Cintas Corp|SELL|9800|27.83 2009-08-21|09:30:38|A1|KRE|SPDR KBW Regional Banking ETF|BUY|9200|21.70 2009-08-21|09:30:39|A1|APA|APACHE CORPORATION|BUY|5700|87.18 2009-08-21|09:30:40|A1|FITB|Fifth Third Bancorp|BUY|9900|10.86 2009-08-21|09:30:40|A1|ICO|INTERNATIONAL COAL GROUP, INC.|SELL|7100|3.45 2009-08-21|09:30:41|A1|NLY|ANNALY CAPITAL MANAGEMENT. INC.|BUY|3000|17.31 2009-08-21|09:30:42|A2|GAZ|iPath Dow Jones - AIG Natural Gas Total Return Sub-Index ETN|SELL|6600|14.09 2009-08-21|09:30:44|A2|CVBF|Cvb Finl|BUY|1100|7.64 2009-08-21|09:30:44|A2|JCP|PENNEY COMPANY, INC.|BUY|300|31.05 2009-08-21|09:30:36|A1|AINV|Apollo Inv Cp|BUY|4500|9.15 

所以例如我希望A1 AINV BUY 9.15的记录总计6800.这是使用自动修复的完美问题。 inheritance我的代码:

 #!/usr/bin/ruby require 'facets' h = Hash.autonew File.open('trades_long.dat','r').each do |line| @date,@time,@account,@ticker,@desc,@type,amount,@price = line.chomp.split('|') if @account != "account" puts "#{amount}" h[@account][@ticker][@type][@price] += amount end #puts sum.to_s end 

问题是无论我如何总结h [@account] [@ ticker] [@ type] [@ price]中的值,它给了我这个错误:

 6000 /usr/local/lib/ruby/gems/1.9.1/gems/facets-2.7.0/lib/core/facets/hash/op_add.rb:8:in `merge': can't convert String into Hash (TypeError) from /usr/local/lib/ruby/gems/1.9.1/gems/facets-2.7.0/lib/core/facets/hash/op_add.rb:8:in `+' from ./trades_consolidaton.rb:13 from ./trades_consolidaton.rb:8:in `each' from ./trades_consolidaton.rb:8 

我尝试过使用不同的“autovivification”方法而没有结果。 这不会发生在perl中! autofvification会知道你想要做什么。 ruby似乎没有这个function。

所以我的问题是,我如何简单地“整合”ruby中的记录。 具体来说,我如何获得以下内容的总数:

H [@account] [@股票] [@类型] [@价格]

非常感谢您的帮助!!

只是为了澄清格伦的解决方案。 这将是完美的,除了它给出(在ruby 1.9中使用标准CSV库的一些修改:

 CSV.foreach("trades_long.dat", :col_sep => "|") do |row| date,time,account,ticker,desc,type,amount,price = *row records[[account,ticker,type,price]] += amount end 

给出以下错误:

 TypeError: String can't be coerced into Fixnum from (irb):64:in `+' from (irb):64:in `block in irb_binding' from /usr/local/lib/ruby/1.9.1/csv.rb:1761:in `each' from /usr/local/lib/ruby/1.9.1/csv.rb:1197:in `block in foreach' from /usr/local/lib/ruby/1.9.1/csv.rb:1335:in `open' from /usr/local/lib/ruby/1.9.1/csv.rb:1196:in `foreach' from (irb):62 from /usr/local/bin/irb:12:in `' 

我同意乔纳斯的观点,你(和萨姆)正在使它变得比它需要的更复杂,但我认为即使他的版本太复杂了。 我只是这样做:

 require 'fastercsv' records = Hash.new(0) FasterCSV.foreach("trades_long.dat", :col_sep => "|") do |row| date,time,account,ticker,desc,type,amount,price = row.fields records[[account,ticker,type,price]] += amount.to_f end 

现在,您有一个哈希值,其中包含帐户,代码,类型和价格的每个唯一组合的总金额。

如果你想要一个以这种方式工作的哈希构建器,你将不得不重新定义+语义。

例如,这很好用:

 class HashBuilder def initialize @hash = {} end def []=(k,v) @hash[k] = v end def [](k) @hash[k] ||= HashBuilder.new end def +(val) val end end h = HashBuilder.new h[1][2][3] += 1 h[1][2][3] += 3 ph[1][2][3] # prints 4 

基本上,您正在尝试将+运算符应用于哈希。

 >> {} + {} NoMethodError: undefined method `+' for {}:Hash from (irb):1 

但是在方面{

 >> require 'facets' >> {1 => 10} + {2 => 20} => {1 => 10, 2 => 20} >> {} + 100 TypeError: can't convert Fixnum into Hash from /usr/lib/ruby/gems/1.8/gems/facets-2.7.0/lib/core/facets/hash/op_add.rb:8:in `merge' from /usr/lib/ruby/gems/1.8/gems/facets-2.7.0/lib/core/facets/hash/op_add.rb:8:in `+' from (irb):6 >> {} += {1 => 2} => {1=>2} >> 

如果要在此时重新定义哈希的+语义,可以执行以下操作:

 class Hash; def +(v); v; end; end 

将此代码段放在原始样本之前,一切都应该正常。 请记住,您正在更改+的已定义行为(注意+未在哈希上定义,其中使用了facet)

看起来你让它变得比以前更加复杂。 我会使用FasterCSV gem和Enumerable#inject这样的东西:

 require 'fastercsv' records=FasterCSV.read("trades_long.dat", :col_sep => "|") records.sort_by {|r| r[3]}.inject(nil) {|before, curr| if !before.nil? && curr[3]==before[3] curr[6]=(curr[6].to_i+before[6].to_i).to_s records.delete(before) end before=curr } 

对于那些在这里找到自己的方式,现在还有另一种选择:

 require 'xkeys' # on rubygems.org h = {}.extend XKeys::Hash ... # Start with 0.0 (instead of nil) and add the amount h[@account, @ticker, @type, @price, :else => 0.0] += amount.to_f 

这将生成可导航的结构。 ( [@account, @ticker, @type, @price]使用[@account, @ticker, @type, @price]数组进行传统键控可能会更好(特殊应用)。 XKeys在写入而不是读取时自动生存,因此查询不存在的元素的结构不会改变结构。