7

Using ruby, how do I make a hash of each letter in the alphabet (keys) and 1-26 (values) ?

I need to create a hash with "a" to "z" in keys and 1 to 26 in values but I do not want to write myself alphabet = {'a'=>1,'b'=>2,....'y'=>25,'z'=>26}

I need this in my code to print alphabet[i] if alphabet.key?(i)

mechnicov
  • 12,025
  • 4
  • 33
  • 56
Aliénor
  • 71
  • 4
  • Does this answer your question? [Generate array of all letters and digits](https://stackoverflow.com/questions/4846853/generate-array-of-all-letters-and-digits) – snakecharmerb Jan 08 '23 at 08:36

5 Answers5

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

Steps:

  • ('a'..'z') - create a Range of alphabetic letters "a" through "z" inclusive
  • each - returns an Enumerator
  • with_index(1) - returns an Enumerator of each element of the initial Range combined with its index (starting at 1) e.g. [["a",1],["b",2],...]
  • to_h - convert the Enumerator to a Hash

Update:

A bit more esoteric but this will also work

enum = Enumerator.produce('a') {|e| e == 'z' ? raise(StopIteration) : e.succ }.tap do |e| 
  e.define_singleton_method(:[]) {|elem| find_index(elem)&.+(1) } 
  e.define_singleton_method(:to_h) { with_index(1).to_h }
end
enum['w']
#=> 23
enum['W']
#=> nil 
enum.to_h 
#=> {"a"=>1, "b"=>2, "c"=>3, "d"=>4, "e"=>5, "f"=>6, "g"=>7, "h"=>8, "i"=>9, "j"=>10, 
#    "k"=>11, "l"=>12, "m"=>13, "n"=>14, "o"=>15, "p"=>16, "q"=>17, "r"=>18, "s"=>19, 
#    "t"=>20, "u"=>21, "v"=>22, "w"=>23, "x"=>24, "y"=>25, "z"=>26}
engineersmnky
  • 25,495
  • 2
  • 36
  • 52
  • You could also produce pairs: `enum = Enumerator.produce(['a', 1]) { |e, i| [e.succ, i.succ] }` and create the hash via `enum.take(26).to_h` – Stefan Dec 16 '22 at 15:30
  • @Stefan yes however the "production" is then limitless which might be more cumbersome. I originally had `enum = Enumerator.produce(['a', 1]) { |a| a.last == 26 ? raise(StopIteration) : a.map(&:succ) }` but then the `find`/`find_index` was less succinct because I had to use block form – engineersmnky Dec 16 '22 at 15:46
7

With two ranges, zip and to_h

('a'..'z').zip(1..26).to_h
mechnicov
  • 12,025
  • 4
  • 33
  • 56
  • I tend to forget about [Enumerable#zip](https://ruby-doc.org/core-2.6.3/String.html), as I'm mostly using [Array#zip](https://ruby-doc.org/core-2.7.0/Array.html#method-i-zip). The former allows for some interesting uses, here one, another being `{a:1, b:2}.zip(1..2) #=> [[[:a, 1], 1], [[:b, 2], 2]]`. – Cary Swoveland Dec 16 '22 at 08:39
  • 1
    You can also use an [endless range](https://ruby-doc.org/core-3.1.2/Range.html#class-Range-label-Endless+Ranges): `('a'..'z').zip(1..).to_h` (for older Ruby versions there's `1.step`) – Stefan Dec 16 '22 at 10:09
5
Hash[('a'..'z').zip(1.upto(26))] 
spickermann
  • 100,941
  • 9
  • 101
  • 131
max
  • 96,212
  • 14
  • 104
  • 165
3

Depending on requirements you may be able to save memory by using an empty hash with a default proc.

h = Hash.new do |_h,k|
  k.is_a?(String) && k.match?(/\A[a-z]\z/) ? (k.ord - 96) : nil
end
  #=> {}
h['a']    #=>  1
h['z']    #=> 26 
h['R']    #=> nil
h['cat']  #=> nil
h[2]      #=> nil
h[{a:1}]  #=> nil

See Hash::new and String#match?.

The regular expression reads, "match the beginning of the string (\A) followed by one lowercase letter ([a-z]) followed by the end of the string (\z). [a-z] denotes a character class.


If all lowercase letters must comprise the hash's keys one may write the following.

('a'..'z').to_h { |c| [c, c.ord - 96] }
  #=> {"a"=>1, "b"=>2,..., "y"=>25, "z"=>26}

See Enumerable#to_h.

Cary Swoveland
  • 106,649
  • 6
  • 63
  • 100
0

There have been better answers given already, but here's an entirely different option using a times loop to simply increment the keys and values of a starter hash using next:

h = {"a" => 1}
25.times {h[h.keys.last.next] = h.values.last.next}
h 
#=>  {"a"=>1, "b"=>2, "c"=>3, "d"=>4, "e"=>5, "f"=>6, "g"=>7, "h"=>8, "i"=>9, "j"=>10, "k"=>11, "l"=>12, "m"=>13, "n"=>14, "o"=>15, "p"=>16, "q"=>17, "r"=>18, "s"=>19, "t"=>20, "u"=>21, "v"=>22, "w"=>23, "x"=>24, "y"=>25, "z"=>26}