Problem: Trying to merge two Ranges into a Hash

D

Drew

I'm trying to take two ranges (namely, (1..26) and ('A'..'Z')) and
merge them into a new hash such that A=>1, B=>2, etc. I know I could
easily do this with a loop but I want to use an iterator and I'm not
quite sure how to approach it. I know this isn't right but it's the
best I ccould come up with:

letters = ('A'..'Z')
numbers = (1..26)
letter_to_number = {}
numbers.each, letters.each {|num, let| letter_to_number[let => num]}

If I use that, I get "1solitaire.rb:32: parse error, unexpected '\n',
expecting tCOLON2 or '[' or '.'"
(line 32 is the last line in the code above)

I'm rather new to Ruby but so far I'm very impressed with it as well as
the community - thanks for any pointers :)
 
G

Gordon Thiesfeld

Drew said:
I'm trying to take two ranges (namely, (1..26) and ('A'..'Z')) and
merge them into a new hash such that A=>1, B=>2, etc. I know I could
easily do this with a loop but I want to use an iterator and I'm not
quite sure how to approach it.

letters = ('A'..'Z').to_a
numbers = (1..26).to_a

letter_to_number = {}

letters.each_with_index{ |letter, index| letter_to_number[letter] =
numbers[index] }
 
M

Martin DeMello

Drew said:
I'm trying to take two ranges (namely, (1..26) and ('A'..'Z')) and
merge them into a new hash such that A=>1, B=>2, etc. I know I could
easily do this with a loop but I want to use an iterator and I'm not
quite sure how to approach it. I know this isn't right but it's the
best I ccould come up with:

irb(main):001:0> numbers = 1..26
=> 1..26
irb(main):002:0> letters = 'a'..'z'
=> "a".."z"
irb(main):003:0> h = {}
=> {}
irb(main):004:0> numbers.zip(letters) {|i, a| h = a}
=> nil
irb(main):005:0> h
=> {16=>"p", 5=>"e", 22=>"v", 11=>"k", 17=>"q", 6=>"f", 23=>"w",
12=>"l", 1=>"a", 18=>"r", 7=>"g", 24=>"x", 13=>"m", 2=>"b", 19=>"s",
8=>"h", 25=>"y", 14=>"n", 3=>"c", 20=>"t", 9=>"i", 26=>"z", 15=>"o",
4=>"d", 21=>"u", 10=>"j"}

martin
 
R

rcoder

Drew said:
I'm trying to take two ranges (namely, (1..26) and ('A'..'Z')) and
merge them into a new hash such that A=>1, B=>2, etc. [...]

Okay, I'm up for a little golfing:
 
E

ekofoed

How about

nr = (1..26).to_a
let=('a'..'z').to_a
h = {}

let.each {|letter| h[letter] = nr.shift}

irb(main):013:0> h
=> {"v"=>22, "k"=>11, "w"=>23, "l"=>12, "a"=>1, "x"=>24, "m"=>13,
"b"=>2, "y"=>25, "n"=>14, "c"=>3, "z"=>26, "o"=>15, "d"=>4, "p"=>16,
"e"=>5, "q"=>17, "f"=>6, "r"=>18, "g"=>7, "s"=>19, "h"=>8, "t"=>20,
"i"=>9, "u"=>21, "j"=>10}

regards

-erik
 
D

Dave Burt

Drew said:
I'm trying to take two ranges (namely, (1..26) and ('A'..'Z')) and
merge them into a new hash such that A=>1, B=>2, etc. I know I could
easily do this with a loop but I want to use an iterator and I'm not
quite sure how to approach it. I know this isn't right but it's the
best I ccould come up with:

letters = ('A'..'Z')
numbers = (1..26)
letter_to_number = {}
numbers.each, letters.each {|num, let| letter_to_number[let => num]}

If all you want is to be able to do is get the answer 1 from
letter_to_number['A'], you can use a proc:

letter_to_number = proc {|c| c[0] - ?A + 1 }

letter_to_number['A'] #=> 1

Cheers,
Dave
 
E

Erik Veenstra

mapping = Hash[*("A".."Z").zip(1..26).flatten]

Providing a solution is probably not helpful if you don't
explain what why this works. Here's the explanation:

"A".."Z"

That's the range "A" up to and including "Z".

("A".."Z").zip(1..26)

The ranges "A".."Z" and 1..26 are zipped to an array of arrays,
like this: [["A", 1], ["B", 2], ...].

("A".."Z").zip(1..26).flatten

Flatten is used to flatten the array of arrays (of arrays (of
arrays)) of objects to an array of objects, resulting in ["A",
1, "B", 2, ...].

*("A".."Z").zip(1..26).flatten

This "*" means "remove the brackets from an array", or "explode
the array", or "turn the array into an argument list". If arr
== [1,2,3] and you want to call Foo.new(1,2,3), you can't do
Foo.new(arr), which is the same as Foo.new([1,2,3]). Instead,
you should do a Foo.new(*arr), which is the same as
Foo.new(1,2,3).

Since Hash::[] expects an argument list and not an array, we
use this "*".

Hash[*("A".."Z").zip(1..26).flatten]

RI says: "Creates a new hash populated with the given objects.
Equivalent to the literal +{ _key_, _value_, ... }+. Keys and
values occur in pairs, so there must be an even number of
arguments."

mapping = Hash[*("A".."Z").zip(1..26).flatten]

Store the result in "mapping" (which should be
"letter_to_number").

I hope this helps.

gegroet,
Erik V. - http://www.erikveen.dds.nl/
 
D

Drew

Erik said:
mapping = Hash[*("A".."Z").zip(1..26).flatten]

Providing a solution is probably not helpful if you don't
explain what why this works. Here's the explanation:

"A".."Z"

That's the range "A" up to and including "Z".

("A".."Z").zip(1..26)

The ranges "A".."Z" and 1..26 are zipped to an array of arrays,
like this: [["A", 1], ["B", 2], ...].

("A".."Z").zip(1..26).flatten

Flatten is used to flatten the array of arrays (of arrays (of
arrays)) of objects to an array of objects, resulting in ["A",
1, "B", 2, ...].

*("A".."Z").zip(1..26).flatten

This "*" means "remove the brackets from an array", or "explode
the array", or "turn the array into an argument list". If arr
== [1,2,3] and you want to call Foo.new(1,2,3), you can't do
Foo.new(arr), which is the same as Foo.new([1,2,3]). Instead,
you should do a Foo.new(*arr), which is the same as
Foo.new(1,2,3).

Since Hash::[] expects an argument list and not an array, we
use this "*".

Hash[*("A".."Z").zip(1..26).flatten]

RI says: "Creates a new hash populated with the given objects.
Equivalent to the literal +{ _key_, _value_, ... }+. Keys and
values occur in pairs, so there must be an even number of
arguments."

mapping = Hash[*("A".."Z").zip(1..26).flatten]

Store the result in "mapping" (which should be
"letter_to_number").

I hope this helps.

gegroet,
Erik V. - http://www.erikveen.dds.nl/

Wow! I really didn't expect much more than "code snippet" and look
what I've gotten. I learned so much about ruby hashes in this thread -
flatten, *, zip, and each_with_index. Thanks so much for all of the
help and explanation, it's greatly appreciated!
 

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
473,774
Messages
2,569,599
Members
45,177
Latest member
OrderGlucea
Top