Generate a hash of all letters and digits - ruby

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)

('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}

With two ranges, zip and to_h
('a'..'z').zip(1..26).to_h

Hash[('a'..'z').zip(1.upto(26))]

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.

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}

Related

Is there a way to reverse individual scrambled words in a string without changing the words' order in Ruby?

I'm trying to reverse a string without using the built-in reverse method to get something like this:
input: "hello, world"
output: "world hello,"
I've been able to reverse the string to "dlrow ,olleh" so the words are the in the order they should be, but I'm stuck on how to reverse the individual words.
Suppose
str = "Three blind mice"
Here are three ways you could obtain the desired result, "mice blind Three", without using the method Array#reverse.
Split string, add index, use Enumerable#sort_by to sort array by index, join words
str.split.each_with_index.sort_by { |_,i| -i }.map(&:first).join(' ')
The steps are as follows.
a = str.split
#=> ["Three", "blind", "mice"]
enum = a.each_with_index
#=> #<Enumerator: ["Three", "blind", "mice"]:each_with_index>
b = enum.sort_by { |_,i| -i }
#=> [["mice", 2], ["blind", 1], ["Three", 0]]
c = b.map(&:first)
#=> ["mice", "blind", "Three"]
c.join(' ')
#=> "mice blind Three"
We can see the elements that will be generated by enum and passed to sort_by by converting enum to an array:
enum.to_a
#=> [["Three", 0], ["blind", 1], ["mice", 2]]
A disadvantage of this method is that it sorts an array, which is a relatively expensive operation. The next two approaches do not share that weakness.
Split string, use Array#values_at to extract words by index, highest to lowest join words
arr = str.split
arr.values_at(*(arr.size-1).downto(0).to_a).join(' ')
The steps are as follows.
arr = str.split
#=> ["Three", "blind", "mice"]
a = arr.size-1
#=> 2
b = a.downto(0).to_a
#=> [2, 1, 0]
c = arr.values_at(*b)
#=> ["mice", "blind", "Three"]
c.join(' ')
#=> "mice blind Three"
Use String#gsub to create an enumerator, chain to Enumerator#with_object, build string
str.gsub(/\w+/).with_object('') { |word,s|
s.prepend(s.empty? ? word : word + ' ') }
The steps are as follows.
enum1 = str.gsub(/\w+/)
#=> #<Enumerator: "Three blind mice":gsub(/\w+/)>
enum2 = enum1.with_object('')
#=> #<Enumerator: #<Enumerator: "Three blind mice":
# gsub(/\w+/)>:with_object("")>
enum2.each { |word,s| s.prepend(s.empty? ? word : word + ' ') }
#=> "mice blind Three"
When String#gsub is called on str without a block it returns an enumerator (see doc). The enumerator generates, and passes to with_object, matches of its argument, /\w+/; that is, words. At this point gsub no longer performs character replacement. When called without a block it is convenient to think of gsub as being named each_match. We can see the values that enum1 generates by converting it to an array (or execute Enumerable#entries on enum1):
enum1.to_a
#=> ["Three", "blind", "mice"]
Though Ruby has no such concept, it may be helpful to think of enum2 as a compound enumerator (study the return value for enum2 = enum1.with_object('') above). It will generate the following values, which is will pass to Enumerator#each:
enum2.to_a
#=> [["Three", ""], ["blind", ""], ["mice", ""]]
The second value of each of these elements is the initial value of the string that will be built and returned by each.
Let's now look at the first element element being generated by enum2 and passed to the block:
word, s = enum2.next
#=> ["Three", ""]
This first step is called Array decomposition.
word
#=> "Three"
s #=> ""
The block calculation is then as follows.
s.empty?
#=> true
t = word
#=> "Three"
s.prepend(t)
#=> "Three"
s #=> "Three"
Now the second element is generated by enum2 and passed to the block
word, s = enum2.next
#=> ["blind", "Three"]
word
#=> "blind"
s #=> "Three"
s.empty?
#=> false
t = word + ' '
#=> "blind "
s.prepend(t)
#=> "blind Three"
Notice that the value of second element of the array returned by enum2.next, the current value of s, has been updated to "Three".
The processing of the third and final element generated by enum2 (["mice", "blind Three"]) is similar, resulting in the block returning the value of s, "mice blind Three".

How do I go backwards a letter?

Using next, I created a method that encrypts a password by advancing every letter of a string one letter forward:
def encryptor
puts "Give me your password!"
password = gets.chomp
index = 0
while index < password.length
password[index] = password[index].next!
index +=1
end
puts password
end
encryptor
I have to create a decrypt method that undoes that. In the end, this should be cleared:
encrypt("abc") should return "bcd"
encrypt("zed") should return "afe"
decrypt("bcd") should return "abc"
decrypt("afe") should return "zed"
I see that Ruby does not have a method to go backwards. I'm stuck with reversing letters. I tried to add an alphabet to index within the method, but I can't get it to do it.
Any help in the right direction would be greatly appreciated.
I know that you can use .next to advance in a string.
Well, kind of, but there are special cases you have to be aware of:
'z'.next #=> 'aa'
I did this successfully
Not quite, your encryptor maps "xyz" to "yzab".
I see that Ruby does not have this option to just go backwards.
Take this example:
'9'.next #=> '10'
'09'.next #=> '10'
As you can see, the mapping is not injective. Both, '9' and '09' are mapped to '10'. Because of this, there is no String#pred – what should '10'.pred return?
Now I'm completely stuck with reversing it a letter.
You could use tr: (both, for encryption and decryption)
'abc'.tr('abcdefghijklmnopqrstuvwxyz', 'zabcdefghijklmnopqrstuvwxy')
#=> 'zab'
tr also has a c1-c2 notation for character ranges, so it can be shortened to:
'abc'.tr('a-z', 'za-y')
#=> 'zab'
Or via Range#to_a, join and rotate:
from = ('a'..'z').to_a.join #=> "abcdefghijklmnopqrstuvwxyz"
to = ('a'..'z').to_a.rotate(-1).join #=> "zabcdefghijklmnopqrstuvwxy"
'abc'.tr(from, to)
#=> "zab"
Another option is to define two alphabets:
from = ('a'..'z').to_a
#=> ["a", "b", "c", ..., "x", "y", "z"]
to = from.rotate(-1)
#=> ["z", "a", "b", ..., "w", "x", "y"]
And create a hash via zip:
hash = from.zip(to).to_h
#=> {"a"=>"z", "b"=>"a", "c"=>"b", ..., "x"=>"w", "y"=>"x", "z"=>"y"}
Which can be passed to gsub:
'abc'.gsub(/[a-z]/, hash)
#=> "zab"
You can also build the regular expression programmatically via Regexp::union:
Regexp.union(hash.keys)
#=> /a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z/
You can use the .next to do this as long as you test for z:
> 'abc'.split("").map { |ch| ch=='z' ? 'a' : ch.next }.join
=> "bcd"
> 'zed'.split("").map { |ch| ch=='z' ? 'a' : ch.next }.join
=> "afe"
Then for decrypt you can do:
> "bcd".split("").map { |ch| ch=='a' ? 'z' : (ch.ord-1).chr }.join
=> "abc"
> "afe".split("").map { |ch| ch=='a' ? 'z' : (ch.ord-1).chr }.join
=> "zed"
Best

Ruby select method selecting values that do not meet criteria

Have the following code which should select every other character of a string and make a new string out of them:
def bits(string)
string.chars.each_with_index.select {|m, index| m if index % 2 == 0}.join
end
However, select returns this output with test case "hello":
"h0l2o4"
When using map instead I get the desired result:
"hlo"
Is there a reason why select would not work in this case? In what scenarios would it be better to use map over select and vice versa
If you still want to use select, try this.
irb(main):005:0> "hello".chars.select.with_index {|m, index| m if index % 2 == 0}.join
=> "hlo"
each_with_index does not work because it is selecting both the character and the index and then joining all of that.
The reason that select does not work in this case is that select "Returns an array containing all elements of enum for which the given block returns a true value" (see the doc here), so what you get in your case is an array of arrays [['h',0],['l',2],['o',4]] which you then join to get "h0l2o4".
So select returns a subset of an enumerable. map returns a one to one mapping of the provided enumerable. For example the following would "fix" your problem by using map to extract character from each value returned by select.
def bits(string)
string.chars.each_with_index.select {|m, index| m if index % 2 == 0}.map { |pair| pair.first }.join
end
puts(bits "hello")
=> hlo
For lots of reasons this is not a good way to get every other character from a string however.
Here is another example using map. In this case each index is mapped to either the character or nil then joined.
def bits(string)
string.chars.each_index.map {|i| string[i] if i.even? }.join
end
If you use Enumerable#map, you will return an array having one element for each character in the string.
arr = "try this".each_char.map.with_index { |c,i| i.even? ? c : nil }
#=> ["t", nil, "y", nil, "t", nil, "i", nil]
which is the same as
arr = "try this".each_char.map.with_index { |c,i| c if i.even? }
#=> ["t", nil, "y", nil, "t", nil, "i", nil]
My initial answer suggested using Array#compact to remove the nils before joining:
arr.compact.join
#=> "tyti"
but as #npn notes, compact is not necessary because Array#join applies NilClass.to_s to the nil's, converting them to empty strings. Ergo, you may simply write
arr.join
#=> "tyti"
Another way you could use map is to first apply Enumerable#each_cons to pass pairs of characters and then return the first character of each pair:
"try this".each_char.each_cons(2).map(&:first).join
#=> "tyti"
Even so, Array#select is preferable, as it returns only the characters of interest:
"try this".each_char.select.with_index { |c,i| i.even? }.join
#=> "tyti"
A variant of this is:
even = [true, false].cycle
#=> #<Enumerator: [true, false]:cycle>
"try this".each_char.select { |c| even.next }.join
#=> "tyti"
which uses Array#cycle to create the enumerator and Enumerator#next to generate its elements.
One small thing: String#each_char is more memory-efficient than String#chars, as the former returns an enumerator whereas the latter creates a temporary array.
In general, when the receiver is an array,
use map when you want to return an array containing one element for each element of the receiver.
use Enumerable#find when you want to return just one element of the receiver.
use Array#select or Array#reject (or Enumerable#select or Enumerable#reject if the receiver is an enumerator).
Me, I'd use a simple regular expression:
"Now is the time to have fun.".scan(/(.)./).join
#=> "Nwi h iet aefn"

All combinations for hash of arrays [duplicate]

This question already has answers here:
Turning a Hash of Arrays into an Array of Hashes in Ruby
(7 answers)
Closed 9 years ago.
Summary
Given a Hash where some of the values are arrays, how can I get an array of hashes for all possible combinations?
Test Case
options = { a:[1,2], b:[3,4], c:5 }
p options.self_product
#=> [{:a=>1, :b=>3, :c=>5},
#=> {:a=>1, :b=>4, :c=>5},
#=> {:a=>2, :b=>3, :c=>5},
#=> {:a=>2, :b=>4, :c=>5}]
When the value for a particular key is not an array, it should simply be included as-is in each resulting hash, the same as if it were wrapped in an array.
Motivation
I need to generate test data given a variety of values for different options. While I can use [1,2].product([3,4],[5]) to get the Cartesian Product of all possible values, I'd rather use hashes to be able to label both my input and output so that the code is more self-explanatory than just using array indices.
I suggest a little pre-processing to keep the result general:
options = { a:[1,2], b:[3,4], c:5 }
options.each_key {|k| options[k] = [options[k]] unless options[k].is_a? Array}
=> {:a=>[1, 2], :b=>[3, 4], :c=>[5]}
I edited to make a few refinements, principally the use of inject({}):
class Hash
def self_product
f, *r = map {|k,v| [k].product(v).map {|e| Hash[*e]}}
f.product(*r).map {|a| a.inject({}) {|h,e| e.each {|k,v| h[k]=v}; h}}
end
end
...though I prefer #Phrogz's '2nd attempt', which, with pre-processing 5=>[5], would be:
class Hash
def self_product
f, *r = map {|k,v| [k].product(v)}
f.product(*r).map {|a| Hash[*a.flatten]}
end
end
First attempt:
class Hash
#=> Given a hash of arrays get an array of hashes
#=> For example, `{ a:[1,2], b:[3,4], c:5 }.self_product` yields
#=> [ {a:1,b:3,c:5}, {a:1,b:4,c:5}, {a:2,b:3,c:5}, {a:2,b:4,c:5} ]
def self_product
# Convert array values into single key/value hashes
all = map{|k,v| [k].product(v.is_a?(Array) ? v : [v]).map{|k,v| {k=>v} }}
#=> [[{:a=>1}, {:a=>2}], [{:b=>3}, {:b=>4}], [{:c=>5}]]
# Create the product of all mini hashes, and merge them into a single hash
all.first.product(*all[1..-1]).map{ |a| a.inject(&:merge) }
end
end
p({ a:[1,2], b:[3,4], c:5 }.self_product)
#=> [{:a=>1, :b=>3, :c=>5},
#=> {:a=>1, :b=>4, :c=>5},
#=> {:a=>2, :b=>3, :c=>5},
#=> {:a=>2, :b=>4, :c=>5}]
Second attempt, inspired by #Cary's answer:
class Hash
def self_product
first, *rest = map{ |k,v| [k].product(v.is_a?(Array) ? v : [v]) }
first.product(*rest).map{ |x| Hash[x] }
end
end
In addition to being more elegant, the second answer is also about 4.5x faster than the first when creating a large result (262k hashes with 6 keys each):
require 'benchmark'
Benchmark.bm do |x|
n = *1..8
h = { a:n, b:n, c:n, d:n, e:n, f:n }
%w[phrogz1 phrogz2].each{ |n| x.report(n){ h.send(n) } }
end
#=> user system total real
#=> phrogz1 4.450000 0.050000 4.500000 ( 4.502511)
#=> phrogz2 0.940000 0.050000 0.990000 ( 0.980424)

How to uniq an array case insensitive

As far as i know, the result of
["a", "A"].uniq
is
["a", "A"]
My question is:
How do I make ["a", "A"].uniq give me either ["a"] or ["A"]
There is another way you can do this. You can actually pass a block to uniq or uniq! that can be used to evaluate each element.
["A", "a"].uniq { |elem| elem.downcase } #=> ["A"]
or
["A", "a"].uniq { |elem| elem.upcase } #=> ["A"]
In this case though, everything will be case insensitive so it will always return the array ["A"]
Just make the case consistent first.
e.g:
["a","A"].map{|i| i.downcase}.uniq
Edit: If as mikej suggests, the elements returned must be exactly the same as in the original array, then this will do that for you:
a.inject([]) { |result,h| result << h unless result.map{|i| i.downcase}.include?(h.downcase); result }
Edit2 Solution which should satisfy mikej :-)
downcased = []
a.inject([]) { |result,h|
unless downcased.include?(h.downcase);
result << h
downcased << h.downcase
end;
result}
you may build a mapping (Hash) between the case-normalized (e.g. downcased) values and the actual value and then take just the values from the hash:
["a", "b", "A", "C"]\
.inject(Hash.new){ |h,element| h[element.downcase] = element ; h }\
.values
selects the last occurrence of a given word (case insensitive):
["A", "b", "C"]
if you want the first occurrence:
["a", "b", "A", "C"]\
.inject(Hash.new){ |h,element| h[element.downcase] = element unless h[element.downcase] ; h }\
.values
["a", "A"].map{|x| x.downcase}.uniq
=> ["a"]
or
["a", "A"].map{|x| x.upcase}.uniq
=> ["A"]
If you are using ActiveSupport, you can use uniq_by.
It doesn't affect the case of the final output.
['A','a'].uniq_by(&:downcase) # => ['A']
A bit more efficient and way is to make use of uniq keys in hashes, so check this:
["a", "A"].inject(Hash.new){ |hash,j| hash[j.upcase] = j; hash}.values
will return the last element, in this case
["A"]
whereas using ||= as assign operator:
["a", "A"].inject(Hash.new){ |hash,j| hash[j.upcase] ||= j; hash}.values
will return first element, in this case
["a"]
especially for big Arrays this should be faster as we don't search the array each time using include?
cheers...
A more general solution (though not the most efficient):
class EqualityWrapper
attr_reader :obj
def initialize(obj, eq, hash)
#obj = obj
#eq = eq
#hash = hash
end
def ==(other)
#eq[#obj, other.obj]
end
alias :eql? :==
def hash
#hash[#obj]
end
end
class Array
def uniq_by(eq, hash = lambda{|x| 0 })
map {|x| EqualityWrapper.new(x, eq, hash) }.
uniq.
map {|x| x.obj }
end
def uniq_ci
eq = lambda{|x, y| x.casecmp(y) == 0 }
hash = lambda{|x| x.downcase.hash }
uniq_by(eq, hash)
end
end
The uniq_by method takes a lambda that checks the equality, and a lambda that returns a hash, and removes duplicate objects as defined by those data.
Implemented on top of that, the uniq_ci method removes string duplicates using case insensitive comparisons.

Resources