红宝石自动复活

发布于 2024-08-07 08:57:05 字数 2846 浏览 7 评论 0原文

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

#!/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 中不会发生!自动激活会知道你想做什么。 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>'

I've been trying to use autovivification in ruby to do simple record consolidation on this:

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

so for example I want the record for A1 AINV BUY 9.15 to have a total of 6800. This is a perfect problem to use autovivification on. So heres my code:

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

The problem is no matter how I try to sum up the value in h[@account][@ticker][@type][@price] it gives me this error:

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

I've tried using different "autovivification" methods with no result. This wouldn't happen in perl! The autofvivification would know what you are trying to do. ruby doesn't seem to have this feature.

So my question really is, how do I perform simply "consolidation" of records in ruby. Specifically, how do I get the total for something like:

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

Many thanks for your help!!

Just to clarify on glenn's solution. That would be perfect except it gives (with a few modifications to use the standard CSV library in ruby 1.9:

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

gives the following error:

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>'

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

白云不回头 2024-08-14 08:57:05

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

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

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

I agree with Jonas that you (and Sam) are making this more complicated than it needs to be, but I think even his version is too complicated. I'd just do this:

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

Now you have a hash with total amounts for each unique combination of account, ticker, type and price.

阳光①夏 2024-08-14 08:57:05

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

例如,这工作得很好:

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

但是在facets{中,

>> 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

将此片段放在原始示例之前,一切都应该很好。请记住,您正在更改 + 的定义行为(注意 + 未在其与构面拉入的哈希上定义)

If you want a hash builder that works that way, you are going to have to redefine the + semantics.

For example, this works fine:

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

Essentially you are trying to apply the + operator to a Hash.

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

However in facets{

>> 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}
>>

If you want to redefine the + semantics for your hash in this occasion you can do:

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

Place this snippet before your original sample and all should be well. Keep in mind that you are changing the defined behavior for + (note + is not defined on Hash its pulled in with facets)

猫弦 2024-08-14 08:57:05

看来你让事情变得比实际情况更复杂了。我会使用 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
}

It looks like you are making it more complicated than it has to be. I would use the FasterCSV gem and Enumerable#inject something like this:

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
}
那支青花 2024-08-14 08:57:05

对于其他在这里找到路的人来说,现在还有另一个选择:

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 在写入时自动激活,而不是在读取时自动激活,因此查询不存在元素的结构不会更改结构。

For others that find their way here, there is now also another option:

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

This will generate a navigable structure. (Traditional keying with arrays of [@account, @ticker, @type, @price] as suggested earlier may be better this particular application). XKeys auto-vivifies on write rather than read, so querying the structure about elements that don't exist won't change the structure.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文