Changing hash key

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

···

--
Posted via http://www.ruby-forum.com/.

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

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

--
Austin Ziegler * halostatue@gmail.com * http://www.halostatue.ca/
               * austin@halostatue.ca * You are in a maze of twisty little passages, all alike. // halo • statue
               * austin@zieglers.ca

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

--
Posted via http://www.ruby-forum.com/\.

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

Todd

···

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

...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 Wed, May 14, 2008 at 11:39 AM, Todd Benson <caduceass@gmail.com> wrote:

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

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.

--
Posted via http://www.ruby-forum.com/\.

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

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

   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

value = list[215..230].delete(' ').to_f/100
# if the character separating - from lv is a space

         # 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

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

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

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 Wed, May 14, 2008 at 11:53 AM, 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 5/14/08, Tim Wolak <tim.wolak@gmail.com> wrote:

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}

···

--
Posted via http://www.ruby-forum.com/\.

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 Wed, May 14, 2008 at 1:35 PM, Adam Shelly <adam.shelly@gmail.com> wrote:

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