【问题标题】:Ruby Autovivification红宝石自动复活
【发布时间】:2010-12-05 04:33:25
【问题描述】:

我一直在尝试在 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。这是使用自动生存的完美问题。所以这是我的代码:

#!/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

我尝试过使用不同的“自动生存”方法,但没有结果。这不会在 perl 中发生! autofvivification 会知道你想要做什么。 ruby 好像没有这个功能。

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

h[@account][@ticker][@type][@price]

非常感谢您的帮助!!

只是为了澄清格伦的解决方案。这将是完美的,除非它提供(在 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 `<main>'

【问题讨论】:

  • 也许你正试图挤进 Perl 方式,如果你走 Ruby 方式会获得更愉快的体验?
  • 您只需要“+= amount.to_f”而不是“+= amount”。
  • 太棒了!谢谢格伦,这很好用!

标签: ruby autovivification


【解决方案1】:

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

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] 数组的传统键控可能在此特定应用程序中更好)。 XKeys 在写入而不是读取时自动激活,因此查询结构中不存在的元素不会改变结构。

【讨论】:

    【解决方案2】:

    我同意 Jonas 的观点,即您(和 Sam)让这件事变得比它需要的更复杂,但我认为即使是他的版本也太复杂了。我会这样做:

    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
    

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

    【讨论】:

    • 这太棒了,除非它不起作用: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' 来自 /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' 来自 /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' 来自 (irb):62
    • @glenn 请查看我编辑的问题,以便更清楚地说明我尝试使用您的方法时发生的事情(我真的很喜欢并希望有效!)
    • 哦,对不起,你只需要一个 .to_f 就可以了。
    • 通过对 amount.to_i 的细微调整,完美运行!谢谢!
    • 就像好/酷一样,也是对_why的致敬。 ejohn.org/blog/eulogy-to-_why “当你不创造东西时,你会被你的品味而不是能力所定义。你的品味只会狭隘并排斥人。所以创造吧。” - _为什么
    【解决方案3】:

    看起来你让它变得比它必须的更复杂。我会使用 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
    }
    

    【讨论】:

      【解决方案4】:

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

      例如,这很好用:

      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
      
      p h[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
      

      将这个 sn-p 放在您的原始样本之前,一切都会好起来的。请记住,您正在更改 + 的已定义行为(注意 + 未在 Hash 上定义,它使用构面拉入)

      【讨论】:

      • 这行得通,但现在我在尝试迭代键时遇到错误:h.each do |key,value| puts "#{value}" end 给出:#<0x10105a8f0>
      猜你喜欢
      • 2017-03-22
      • 2012-12-29
      • 2019-08-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-09-09
      • 1970-01-01
      相关资源
      最近更新 更多