I've been looking for a way to change a hash key. In my code I'm trying
to change the key from a number to a string. Do I need to convert the
key to a string and put it new value in there or is my code wrong?
Thanks in advance,
Tim
def numbers
@skty = Hash.new(0)
@sktynl.each do |key, value|
if key <= "39"
key = "SKTY"
@skty[key] += value # !> instance variable @skty
not initialized
elsif key >="40"
key = "SKYNY"
@skty[key] += value
end
on 14.05.2008 16:52
on 14.05.2008 17:04
On Wed, May 14, 2008 at 10:53 AM, Tim Wolak <tim.wolak@gmail.com> wrote: > I've been looking for a way to change a hash key. In my code I'm trying > to change the key from a number to a string. Do I need to convert the > key to a string and put it new value in there or is my code wrong? > end First, your comparison isn't against numbers, but against strings: irb(main):001:0> "5" <= "40" => false If your keys are truly numbers, use numeric comparisons. Second, I suspect the problem isn't that @skty (which is a *really* bad variable name, by the way) isn't initialized, but that @skty[key] isn't initialized. Which is true because you're not initializing the value, you're just providing a default value if the key isn't found. If you need to initialize an unfound value, you need the block form of Hash.new: @skty = Hash.new { |h, k| h[k] = 0 } -austin
on 14.05.2008 17:24
I posted that code after I fixed an error. It is getting initialized, after I iterate over the hash its still printing out the numbers that are account numbers for the keys. I need to get any numbers that are less than 39 and put that in a new hash with the key as SKTY and all values added together into a value for that key. Hope that make more sense of that I'm trying to do. Tim > > @skty = Hash.new { |h, k| h[k] = 0 } > > -austin
on 14.05.2008 18:39
On Wed, May 14, 2008 at 10:24 AM, Tim Wolak <tim.wolak@gmail.com> wrote: > I posted that code after I fixed an error. It is getting initialized, > after I iterate over the hash its still printing out the numbers that > are account numbers for the keys. I need to get any numbers that are > less than 39 and put that in a new hash with the key as SKTY and all > values added together into a value for that key. > > Hope that make more sense of that I'm trying to do. > > Tim Let me see. You want to have a new hash called skty with keys skty and sktny? That's confusing, mostly I think because of your choice of variable names. If you can be sure that all keys are comparable with each other (in this case integers), here's very quick and dirty... class Hash def sum_values values.inject {|s, i| s + i} end def partition_by part h1 = self.reject {|k,v| k > part} h2 = self.reject {|k,v| k <= part} return h1, h2 end end h = Hash[1,5,2,6,3,7,4,8,5,9] a, b = h.partition_by 3.5 h_new = {} h_new['skty'] = a.sum_values h_new['sktny'] = b.sum_values p h_new You could most of this with one line if you were so crazily inclined :-) Todd
on 14.05.2008 18:45
On Wed, May 14, 2008 at 11:39 AM, Todd Benson <caduceass@gmail.com> wrote: > > end ...or maybe better... class Array def sum inject {|s, i| s + i} end end ..and then later... h_new['skty'] = a.values.sum All this would need to be refactored, of course, to be production-worthy. Todd
on 14.05.2008 18:53
Hmm ok then maybe changing the keys is not what I need to do then.
The reason I have skty and sktyny is that those are account names, sorry
should have stated that earlier. So I have a list of account numbers
and their balances, all accounts listed 500 through 539 should have a
key of skty and accounts with 540 through 550 should be sktyny. All the
account balances for skty should be combined into the value for skty
and all the balances should be combined for teh value of sktyny. Here
is the full code, I did not want to post a mile long request with it
all in there.
Tim
class SktyFut
attr_reader :acct
def initialize(filename)
@acct = File.new(filename, "r")
end
def future_data
@sktylist = Hash.new(0)
@acct.each do |list|
office = list[21..23]
if office == "RPT"
next
else
acctnum = list[24..28]
end
lv = list[217..230]
is_negative = list[215,1] == "-"
value = lv.to_f/100
value = -value if is_negative
# Add vales to hash
@sktylist[acctnum] += value
end
return @sktylist
end
end
class Calculate
attr_reader :sktyfuta, :sktyfutb
def initialize(sktyfuta, sktyfutb)
@sktyfuta = sktyfuta
@sktyfutb = sktyfutb
end
def data_comp
@sktyfuta.merge(@sktyfutb) { |key, old_value,
new_value| old_value - new_value }
end
#end
end
class FinalNum
attr_reader :sktynl
def initialize(sktynl)
@sktynl = sktynl
end
def numbers
@skty = Hash.new(0)
@sktynl.each do |key, value|
if key <= "539"
key.to_s
key = "SKTY"
@skty[key] += value
elsif key >="540"
key = "SKYNY"
@skty[key] += value
end
#@skty.each{ |key, value| puts "#{key} value
#{value}" }
end
end
end
Dir.chdir("/tmp")
post = SktyFut.new("SKTYFutBal20080513.txt")
a = post.future_data
#a.each{|key, value| puts "#{key} value is #{value}"}
pre = SktyFut.new("SKTYFutBal20080512.txt")
b = pre.future_data
data = Calculate.new(a,b)
iteration = data.data_comp
iteration.sort
#iteration.each{|key, value| puts "#{key} comp equals #{value}" }
sktyfinal = FinalNum.new(iteration)
submission = sktyfinal.numbers
submission.each{ |key, value| puts "#{key} line is #{value}" }
>
> Let me see. You want to have a new hash called skty with keys skty
> and sktny? That's confusing, mostly I think because of your choice of
> variable names.
on 14.05.2008 20:01
On Wed, May 14, 2008 at 11:53 AM, Tim Wolak <tim.wolak@gmail.com> wrote: > > Tim > > > > class SktyFut > attr_reader :acct > > def initialize(filename) > @acct = File.new(filename, "r") I'm not sure ruby automagically closes the file when the instance of SktyFut is garbage collected. You open a file and assign it to a class instance variable. Looks a little scary to me. > end > lv = list[217..230] > is_negative = list[215,1] == "-" > value = lv.to_f/100 > value = -value if is_negative value = list[215..230].delete(' ').to_f/100 # if the character separating - from lv is a space > attr_reader :sktyfuta, :sktyfutb > end > if key <= "539" > key.to_s > key = "SKTY" > @skty[key] += value replace above 3 lines with... @skty["SKTY"] += value > elsif key >="540" > key = "SKYNY" > @skty[key] += value replace above 3 lines with... @skty["SKYNY"] += value or SKTYNY or whatever it is you are using > end > #@skty.each{ |key, value| puts "#{key} value > #{value}" } > Here should be... return @skty > data = Calculate.new(a,b) > iteration = data.data_comp > iteration.sort > #iteration.each{|key, value| puts "#{key} comp equals #{value}" } > sktyfinal = FinalNum.new(iteration) > submission = sktyfinal.numbers > submission.each{ |key, value| puts "#{key} line is #{value}" } For the #numbers method, you might someday want to check out #select. For example. skny_sum = @sktynl.select {|k, v| k < 540}.inject(0) {|sum, arr| sum + arr[1]} hth, Todd
on 14.05.2008 20:35
On 5/14/08, Tim Wolak <tim.wolak@gmail.com> wrote: > Hmm ok then maybe changing the keys is not what I need to do then. > > The reason I have skty and sktyny is that those are account names, sorry > should have stated that earlier. You can still use readable variable names in the code. (What if you wanted to run the same algorithm on two different accounts?). A better idea might be to name your hash @account_balances, and store the account names as strings. So I have a list of account numbers > and their balances, all accounts listed 500 through 539 should have a > key of skty and accounts with 540 through 550 should be sktyny. All the > account balances for skty should be combined into the value for skty > and all the balances should be combined for teh value of sktyny. So what about something like account_map = { "sticky" => (500..539), "stinky" => (540..550) } # fill @account_balances... account_summary= Hash.new account_map.each{|name,range| account_summary[name]= @account_balances.select{|act_num, balance| range.include? (act_num) } } account_summary.each{|name, subtotals| total = subtotals.map{|act_num,balance|balance}.sum puts "#{name} has a balance of #{total}" } -Adam
on 15.05.2008 00:15
On Wed, May 14, 2008 at 1:35 PM, Adam Shelly <adam.shelly@gmail.com> wrote: > } > } > account_summary.each{|name, subtotals| > total = subtotals.map{|act_num,balance|balance}.sum Just FYI to ruby nubies: The #sum method for addition in enumerables is available in libraries (included with Rails, for example), but not in ruby 1.8.6 itself. I don't know the status of ruby 1.9 on that one. Todd
on 15.05.2008 06:54
Tim Wolak wrote: > Hmm ok then maybe changing the keys is not what I need to do then. > > The reason I have skty and sktyny is that those are account names, sorry > should have stated that earlier. So I have a list of account numbers > and their balances, all accounts listed 500 through 539 should have a > key of skty and accounts with 540 through 550 should be sktyny. All the > account balances for skty should be combined into the value for skty > and all the balances should be combined for teh value of sktyny. Here > is the full code, I did not want to post a mile long request with it > all in there. > #Create a hash so a non-existent key returns #the default value 0: results = Hash.new {|hash, key| hash[key] = 0} key1 = "skty" range1 = 500..539 key2 = "sktyny" range2 = 540..550 DATA.each_line do |line| acct_str, balance_str = line.split acct = acct_str.to_i balance = balance_str.to_f if range1 === acct results[key1] += balance elsif range2 === acct results[key2] += balance else results[acct] = balance end end p results __END__ 440 550.25 539 22.25 500 10.50 540 100.00 550 225.00 100 300.00 --output:-- {"sktyny"=>325.0, 440=>550.25, 100=>300.0, "skty"=>32.75}