build hash by iterating

J

Jason Lillywhite

I am building a hash this way:

h = {}
i = 0
j = 24

while i < 15 do
h = 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!
 
A

ara.t.howard

I am building a hash this way:

h = {}
i = 0
j = 24

while i < 15 do
h = 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!




i might do


cfp:~ > cat a.rb
h = {} and 15.times{|i| h = (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/
 
J

James Coglan

[Note: parts of this message were removed to make it a legal post.]
h = {}
i = 0
j = 24

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



h = (0...15).inject({}) do |m,i|
m = 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 }
 
E

Einar Magnús Boson

I am building a hash this way:

h = {}
i = 0
j = 24

while i < 15 do
h = 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!



h = (0...15).inject({}) {|h, i| h = (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
 
B

Brian Candler

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

Jason Lillywhite

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

Todd Benson

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
 
P

Peña, Botp

RnJvbTogSmFzb24gTGlsbHl3aGl0ZSBbbWFpbHRvOmphc29uLmxpbGx5d2hpdGVAZ21haWwuY29t
XSANCiMgSG93ZXZlciwgaXRlcmF0aW5nIG92ZXIgYSBoYXNoIGlzIGNvbmZ1c2luZyBtZS4gSGVy
ZSBpcyANCiMgYSBzaW1wbGUgZXhhbXBsZToNCiMgDQojIGhhc2ggPSANCiMgW1s6ZGlhbWV0ZXIs
IDQ1XSwgWzppZCwgMl1dLmluamVjdCh7fSkgZG8gfHJlc3VsdCwgZWxlbWVudHwNCiMgICByZXN1
bHRbZWxlbWVudC5maXJzdF0gPSBlbGVtZW50Lmxhc3QNCiMgICByZXN1bHQNCiMgZW5kDQojIA0K
IyBjYW4gc29tZW9uZSBoZWxwIG1lIHVuZGVyc3RhbmQgYmV0dGVyIHdoYXQgZXhhY3RseSBpcyAN
CiMgaGFwcGVuaW5nIG9uIGVhY2ggDQojIGl0ZXJhdGlvbj8NCiMgaGFzaChpPTEpID0gPz8NCiMg
aGFzaChpPTIpID0gPz8NCg0KaW5pdGlhbDoNCiAgcmVzdWx0ID0ge30NCg0KaXRlciAxOg0KICBy
ZXN1bHRbOmRpYW1ldGVyXSA9IDQ1DQoNCml0ZXIgMjogDQogIHJlc3VsdFs6aWRdID0gMg0KDQoN
CmkgdGhpbmsgdGhlIGNvbmZ1c2lvbiBzdGVtcyBhcm91bmQgdGhlIGxvbmVseSBsaW5lIGNvbnRh
aW5pbmcgInJlc3VsdCIuIFlvdSdsbCBuZWVkIHRoYXQgYmVjYXVzZSBpbmplY3Qgd2lsbCByZXRy
aWV2ZSBhbmQgdXNlIHRoZSB2YWx1ZSBvZiB0aGUgY29kZSBibG9jayBhcyB0aGUgbmV4dCAicmVz
dWx0Ii4gKHRyeSB0ZXN0aW5nIGl0IGJ5IHJlbW92aW5nIHRoZSAicmVzdWx0IiBsaW5lIDopDQoN
Cml0IGlzIGluIHVuaXF1ZSBjYXNlcyBsaWtlIHRoZXNlIHRoYXQgaSBmaW5kIGluamVjdCBub3Qg
dG9vIGludHVpdGl2ZSA7KQ0KDQppIG1pZ2h0IGJlIGJldHRlciBvZmYgd2l0aCwNCg0KaGFzaCA9
IHt9DQpbWzpkaWFtZXRlciwgNDVdLCBbOmlkLCAyXV0uZWFjaCBkbyB8ayx2fA0KICAgaGFzaFtr
XSA9IHYNCmVuZA0KDQpvcg0KDQpoYXNoID0gSGFzaFsgKiBbWzpkaWFtZXRlciwgNDVdLCBbOmlk
LCAyXV0gLiBmbGF0dGVuIF0NCg0K
 
R

Robert Klemme

From: Jason Lillywhite [mailto:[email protected]]
you're indexing normally, why not use an array?

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
 
B

Brian Candler

Jason said:
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.
 
B

Brian Candler

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.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,431
Messages
2,571,679
Members
48,796
Latest member
Greg L.

Latest Threads

Top