Build hash by iterating

I am building a hash this way:

h = {}
i = 0
j = 24

while i < 15 do
  h[i] = j
  i += 1
  j += 6
end

=> {5=>54, 11=>90, 0=>24, 6=>60, 12=>96, 1=>30, 7=>66, 13=>102, 2=>36,
8=>72, 14=>108, 3=>42, 9=>78, 4=>48, 10=>84}

Just what I need, but doesn't seem very Ruby-friendly

And if I want to change each hash value from fixnum to string, how would
that be done?

this is not working: new_hash = h.each_key {|k| h[k].to_s}

thank you!

···

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

i might do

cfp:~ > cat a.rb
h = {} and 15.times{|i| h[i] = (24 + (i * 6))}

p h.sort

p({5=>54, 11=>90, 0=>24, 6=>60, 12=>96, 1=>30, 7=>66, 13=>102, 2=>36, 8=>72, 14=>108, 3=>42, 9=>78, 4=>48, 10=>84}.sort)

cfp:~ > ruby a.rb
[[0, 24], [1, 30], [2, 36], [3, 42], [4, 48], [5, 54], [6, 60], [7, 66], [8, 72], [9, 78], [10, 84], [11, 90], [12, 96], [13, 102], [14, 108]]
[[0, 24], [1, 30], [2, 36], [3, 42], [4, 48], [5, 54], [6, 60], [7, 66], [8, 72], [9, 78], [10, 84], [11, 90], [12, 96], [13, 102], [14, 108]]

a @ http://codeforpeople.com/

···

On Nov 19, 2008, at 8:31 AM, Jason Lillywhite wrote:

I am building a hash this way:

h = {}
i = 0
j = 24

while i < 15 do
h[i] = j
i += 1
j += 6
end

=> {5=>54, 11=>90, 0=>24, 6=>60, 12=>96, 1=>30, 7=>66, 13=>102, 2=>36,
8=>72, 14=>108, 3=>42, 9=>78, 4=>48, 10=>84}

Just what I need, but doesn't seem very Ruby-friendly

And if I want to change each hash value from fixnum to string, how would
that be done?

this is not working: new_hash = h.each_key {|k| h[k].to_s}

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

--
we can deny everything, except that we have the possibility of being better. simply reflect on that.
h.h. the 14th dalai lama

h = {}
i = 0
j = 24

while i < 15 do
h[i] = j
i += 1
j += 6
end

h = (0...15).inject({}) do |m,i|
   m[i] = 24 + i * 6
   m
end

this is not working: new_hash = h.each_key {|k| h[k].to_s}

new_hash = h.inject({}) { |m,p| m[p[0].to_s] = p[1]; m }

h = (0...15).inject({}) {|h, i| h[i] = (24+i*6).to_s; h}
p h

# but this hash makes no sense.
# just do

h = Array.new(15) {|i| (24+i*6).to_s}
p h

#then you can index the array:
p h[11]

p h[5]

# >> {5=>"54", 11=>"90", 0=>"24", 6=>"60", 12=>"96", 1=>"30", 7=>"66", 13=>"102", 2=>"36", 8=>"72", 14=>"108", 3=>"42", 9=>"78", 4=>"48", 10=>"84"}
# >> ["24", "30", "36", "42", "48", "54", "60", "66", "72", "78", "84", "90", "96", "102", "108"]
# >> "90"
# >> "54"

einarmagnus

···

On 19.11.2008, at 15:31 , Jason Lillywhite wrote:

I am building a hash this way:

h = {}
i = 0
j = 24

while i < 15 do
h[i] = j
i += 1
j += 6
end

=> {5=>54, 11=>90, 0=>24, 6=>60, 12=>96, 1=>30, 7=>66, 13=>102, 2=>36,
8=>72, 14=>108, 3=>42, 9=>78, 4=>48, 10=>84}

Just what I need, but doesn't seem very Ruby-friendly

And if I want to change each hash value from fixnum to string, how would
that be done?

this is not working: new_hash = h.each_key {|k| h[k].to_s}

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

And if I want to change each hash value from fixnum to string, how would
that be done?

this is not working: new_hash = h.each_key {|k| h[k].to_s}

The simple two-liner is this:

  new_hash = {}
  h.each { |k,v| new_hash[k] = v.to_s }

The one-liner uses 'inject':

  new_hash = h.inject({}) { |n,(k,v)| n[k] = v.to_s; n }

To understand 'inject', note that:
1. you pass in the new object for accumulating the result ({})
2. for each call of the block, the accumulator is passed as
   the first argument (n)
3. the return value of the block is used as the new accumulator
   (for the next iteration, or as the final result). In this
   case, we wish to continue using n, so we return that as the
   value of the block.

···

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

# I am building a hash this way:
# h = {}
# i = 0
# j = 24
# while i < 15 do
# h[i] = j
# i += 1
# j += 6
# end
# => {5=>54, 11=>90, 0=>24, 6=>60, 12=>96, 1=>30, 7=>66,
# 13=>102, 2=>36, 8=>72, 14=>108, 3=>42, 9=>78, 4=>48, 10=>84}
# Just what I need, but doesn't seem very Ruby-friendly
# And if I want to change each hash value from fixnum to
# string, how would that be done?
# this is not working: new_hash = h.each_key {|k| h[k].to_s}

you're indexing normally, why not use an array?

ar=Array.new(15){|x| 24+x*6}

=> [24, 30, 36, 42, 48, 54, 60, 66, 72, 78, 84, 90, 96, 102, 108]

ar[0]

=> 24

ar[5]

=> 54

w added adv that you can refer in groups

ar[0..5]

=> [24, 30, 36, 42, 48, 54]

no need to rush, you can convert to hash anytime :wink:

hh=Hash[*ar.each_with_index.to_a.flatten]

=> {60=>6, 66=>7, 72=>8, 78=>9, 84=>10, 90=>11, 24=>0, 96=>12, 30=>1, 102=>13, 36=>2, 108=>14, 42=>3, 48=>4, 54=>5}

···

From: Jason Lillywhite [mailto:jason.lillywhite@gmail.com]

perfect. Thank you.

···

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

Thank you. Enumerable#inject has always been scary to me. I'm having a
hard time following what you did here. Luckily, I found this great page:

···

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

Botp, thanks. That is an easier way to look at it. Can I go back to this
Enumerable#inject? I understand iterating on elements of an array like
this:

range = (1..4)
sum = range.inject(0) {|result, element| result += element }

#I understand inject to be doing this (where my 'i' means 'iteration'):
#sum(i=1) = 0 + 1 = 1
#sum(i=2) = sum(i=1) + 2 = 3
#sum(i=3) = sum(i=2) + 3 = 6
#sum(i=4) = sum(i=3) + 4 = 10
=> sum = 10

#However, iterating over a hash is confusing me. Here is a simple
example:

hash = [[:diameter, 45], [:id, 2]].inject({}) do |result, element|
  result[element.first] = element.last
  result
end

#can someone help me understand better what exactly is happening on each
iteration?
#hash(i=1) = ??
#hash(i=2) = ??

Thank you!

···

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

I'd even go as far as to question the whole collection approach: since there is a fairly easy mathematical relationship between key and value why not just define a function that calculates value from key on demand?

def f(x) 24 + 6 * x end

Kind regards

  robert

···

On 20.11.2008 02:39, Peña wrote:

From: Jason Lillywhite [mailto:jason.lillywhite@gmail.com]

you're indexing normally, why not use an array?

Botp, thanks. That is an easier way to look at it. Can I go back to this
Enumerable#inject? I understand iterating on elements of an array like
this:

range = (1..4)
sum = range.inject(0) {|result, element| result += element }

Don't use +=, just +
You are injecting the result of the block and not changing the value
of "result" _inside_ the block. That variable is temporary.

#I understand inject to be doing this (where my 'i' means 'iteration'):
#sum(i=1) = 0 + 1 = 1
#sum(i=2) = sum(i=1) + 2 = 3
#sum(i=3) = sum(i=2) + 3 = 6
#sum(i=4) = sum(i=3) + 4 = 10
=> sum = 10

#However, iterating over a hash is confusing me. Here is a simple
example:

hash = [[:diameter, 45], [:id, 2]].inject({}) do |result, element|
result[element.first] = element.last
result
end

You are not iterating over a hash here, you are iterating over the
array [[:diameter, 45], [:id, 2]]

#can someone help me understand better what exactly is happening on each
iteration?
#hash(i=1) = ??
#hash(i=2) = ??

a = 1, 2, 3, 4
#start inject with an initial empty hash
h = a.inject({}) {|s, e| s[e.to_s] = e; s}
#just assigns keys to values, but the keys are strings.

#inject gives you the result of the block (in this case s) on each
iteration as your first inserted object (s) on each go. What you do
with each element and the result of the block on each iteration is up
to you. I wish I could give you a better example, but there are some
experts here that can probably explain it more thoroughly.

Todd

···

On Wed, Nov 19, 2008 at 10:01 PM, Jason Lillywhite <jason.lillywhite@gmail.com> wrote:

# However, iterating over a hash is confusing me. Here is
# a simple example:

···

From: Jason Lillywhite [mailto:jason.lillywhite@gmail.com]
#
# hash =
# [[:diameter, 45], [:id, 2]].inject({}) do |result, element|
# result[element.first] = element.last
# result
# end
#
# can someone help me understand better what exactly is
# happening on each
# iteration?
# hash(i=1) = ??
# hash(i=2) = ??

initial:
  result = {}

iter 1:
  result[:diameter] = 45

iter 2:
  result[:id] = 2

i think the confusion stems around the lonely line containing "result". You'll need that because inject will retrieve and use the value of the code block as the next "result". (try testing it by removing the "result" line :slight_smile:

it is in unique cases like these that i find inject not too intuitive :wink:

i might be better off with,

hash = {}
[[:diameter, 45], [:id, 2]].each do |k,v|
   hash[k] = v
end

or

hash = Hash[ * [[:diameter, 45], [:id, 2]] . flatten ]

Jason Lillywhite wrote:

range = (1..4)
sum = range.inject(0) {|result, element| result += element }

That should be:

  range = (1..4)
  sum = range.inject(0) {|result, element| result + element }

To understand this fully, I will write out what inject is doing in
longhand:

  range = (1..4)
  tmp = 0 # the (0) bit
  range.each do |element|
    result = tmp
    tmp = result + element
    #(A)# ######(B)#######
  end
  sum = tmp # final value of inject

#(B)# is the execution of the block body. #(A)# is done implicitly by
'inject': it stores the value calculated by the block, and then passes
this into the next iteration, or else uses it as the final return value.

#However, iterating over a hash is confusing me. Here is a simple
example:

hash = [[:diameter, 45], [:id, 2]].inject({}) do |result, element|
  result[element.first] = element.last
  result
end

#can someone help me understand better what exactly is happening on each
iteration?

Written out longhand as above:

  tmp = {}
  [[:diameter, 45], [:id, 2]].each do |element|
    result = tmp
    result[element.first] = element.last
    tmp = result
    #(A)# #(B)##
  end
  hash = tmp

To start with the accumulator is set to an empty hash.

After one iteration, you have done hash[:diameter] = 45, so you've added
a new element to the hash. You then give the entire hash object as the
value result from the block, so that it is passed in as 'result' to the
next iteration.

On the next iteration, you do hash[:id] = 2, so you've added a new value
to it. But the same hash object is the result.

In this case, for every iteration the *same* hash object is passed in,
and returned so that it can be used by the following iteration. What
you're doing is modifying that object as a side-effect of the block
executing.

Now, it is possible to get the same result without modifying the hash,
but instead creating a new hash object in each iteration, like this:

  hash = [[:diameter, 45], [:id, 2]].inject({}) do |result, element|
    result.merge({element.first => element.last})
  end

This is what a 'functional' programmer would do, where functions cannot
modify data, only create new data. In each iteration you're merging the
hash built so far with a new one-element hash, to create a new partial
result which is one element larger.

This is less efficient, as you're repeatedly creating larger and larger
hash objects only to be garbage-collected later. But if you were doing
this in (say) Erlang, that's what you'd need to do.

Hope this is a bit clearer now...

Brian.

···

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

Written out longhand as above:

Perhaps I should have done this more clearly:

  tmp = {}
  [[:diameter, 45], [:id, 2]].each do |element|
    result = tmp
    tmp = (
      result[element.first] = element.last
      result
    )
  end
  hash = tmp

The part in parentheses is the block, and you can see that the overall
value from executing the block is assigned to 'tmp' to be used in the
next iteration.

···

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

very excellent descriptions. All of this was extremely helpful! Thank
you.

···

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