1

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

さまざまな「自動生存化」方法を試してみましたが、結果はありませんでした。これは perl では起こりません! autofvivification は、あなたが何をしようとしているのかを知っています。Rubyにはこの機能がないようです。

だから私の質問は、どうすればRubyでレコードの単純な「統合」を実行できるかということです。具体的には、次のような合計を取得するにはどうすればよいですか。

h[@アカウント][@ティッカー][@タイプ][@価格]

助けてくれて本当にありがとうございます!!

グレンの解決策を明確にするために。それが与えることを除いて、それは完璧です(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>'
4

4 に答える 4

5

あなた (とサム) がこれを必要以上に複雑にしているというジョナスの意見には同意しますが、彼のバージョンでさえ複雑すぎると思います。私はこれをするだけです:

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

これで、アカウント、ティッカー、タイプ、価格の一意の組み合わせごとに合計金額のハッシュが得られました。

于 2009-10-09T14:45:45.243 に答える
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

このスニペットを元のサンプルの前に配置すると、すべてがうまくいくはずです。+ の定義された動作を変更していることに注意してください (+ は、ファセットでプルされた Hash では定義されていないことに注意してください)。

于 2009-10-08T23:56:29.163 に答える
0

必要以上に複雑にしているようです。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
}
于 2009-10-09T14:11:37.930 に答える
0

ここで道を見つけた他の人のために、別のオプションもあります。

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読み取りではなく書き込みで自動有効化されるため、存在しない要素について構造を照会しても、構造は変更されません。

于 2013-07-26T01:49:53.703 に答える