[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

Problem: Trying to merge two Ranges into a Hash

Drew

6/14/2006 3:33:00 AM

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

7 Answers

Gordon Thiesfeld

6/14/2006 5:04:00 AM

0


Drew wrote:
> 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] }

Martin DeMello

6/14/2006 5:48:00 AM

0

Drew <latortuga@gmail.com> wrote:
> 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[i] = 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

rcoder@gmail.com

6/14/2006 7:10:00 AM

0

Drew wrote:
> 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:

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

Sorry, it must be late...that's probably not terribly helpful.

-rcoder

ekofoed

6/14/2006 8:25:00 AM

0

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

Drew wrote:
> 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 :)

Dave Burt

6/14/2006 8:35:00 AM

0

Drew wrote:
> 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

Erik Veenstra

6/14/2006 8:44:00 PM

0

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

Drew

6/15/2006 3:24:00 AM

0


Erik Veenstra wrote:
> > 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.erikve...

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!