Reputation: 161
I am trying to say if x == consonants[0] or [1] or [2]
all the way to [21]
on one line. For some reason I thought consonants[0..21]
would work but it doesn't:
consonants = ["b", "c", "d", "f", "g", "h", "j", "k", "l", "m", "n", "p", "q", "r", "s", "t",
"v", "w", "x", "y", "z"]
new_first = ["m", "a", "t", "t", "h", "e", "w", "s"]
new_first.each do |x|
if x == consonants[0]
puts x.next!
elsif x == consonants[1]
puts x.next!
elsif x == consonants[2]
puts x.next!
elsif x == consonants[3]
puts x.next!
else
puts "test failed"
end
end
Upvotes: 0
Views: 99
Reputation: 160551
Consider this:
consonants = ('a' .. 'z').to_a - %w[a e i o u] # => ["b", "c", "d", "f", "g", "h", "j", "k", "l", "m", "n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z"]
shifted_consonants = consonants.zip(consonants.rotate).to_h # => {"b"=>"c", "c"=>"d", "d"=>"f", "f"=>"g", "g"=>"h", "h"=>"j", "j"=>"k", "k"=>"l", "l"=>"m", "m"=>"n", "n"=>"p", "p"=>"q", "q"=>"r", "r"=>"s", "s"=>"t", "t"=>"v", "v"=>"w", "w"=>"x", "x"=>"y", "y"=>"z", "z"=>"b"}
'matthews'.chars.map{ |c| shifted_consonants[c] || c } # => ["n", "a", "v", "v", "j", "e", "x", "t"]
That took the range of 'a'
to 'z'
and converted it to an array, then subtracted the array of vowels, resulting in only consonants.
Next, it turned the array of consonants into a hash/look-up table shifted_consonants
where each key is a current consonant and the value is the next consonant.
Finally, it takes each character in 'matthews'
and looks to see if there is a value in shifted_consonants
for that character. If not, nil
is returned, which triggers ||
and returns the current character. If there is a hit in the hash, the next value for that consonant is returned, which short-circuits the ||
"or".
An alternate would be to take advantage of tr
:
consonants = (('a' .. 'z').to_a - %w[a e i o u]).join # => "bcdfghjklmnpqrstvwxyz"
shifted_consonants = consonants.chars.rotate.join # => "cdfghjklmnpqrstvwxyzb"
'matthews'.tr(consonants, shifted_consonants).chars # => ["n", "a", "v", "v", "j", "e", "x", "t"]
Checking for speed:
CONSONANTS = ('a' .. 'z').to_a - %w[a e i o u] # => ["b", "c", "d", "f", "g", "h", "j", "k", "l", "m", "n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z"]
SHIFTED_CONSONANTS_HASH = CONSONANTS.zip(CONSONANTS.rotate).to_h # => {"b"=>"c", "c"=>"d", "d"=>"f", "f"=>"g", "g"=>"h", "h"=>"j", "j"=>"k", "k"=>"l", "l"=>"m", "m"=>"n", "n"=>"p", "p"=>"q", "q"=>"r", "r"=>"s", "s"=>"t", "t"=>"v", "v"=>"w", "w"=>"x", "x"=>"y", "y"=>"z", "z"=>"b"}
CONSONANTS_STR = (('a' .. 'z').to_a - %w[a e i o u]).join # => "bcdfghjklmnpqrstvwxyz"
SHIFTED_CONSONANTS_STR = CONSONANTS_STR.chars.rotate.join # => "cdfghjklmnpqrstvwxyzb"
require 'fruity'
sample_string = 'matthews'
compare do
use_hash { sample_string.chars.map{ |c| SHIFTED_CONSONANTS_HASH[c] || c } }
use_tr { sample_string.tr(CONSONANTS_STR, SHIFTED_CONSONANTS_STR).chars }
end
# >> Running each test 2048 times. Test will take about 1 second.
# >> use_tr is faster than use_hash by 10.000000000000009% ± 10.0%
The longer the sample string is, the greater the difference. Changing to:
sample_string = 'matthews' * 1000
I see a result of:
# >> Running each test 4 times. Test will take about 1 second.
# >> use_tr is faster than use_hash by 4x ± 0.1
Found in a comment, NOT in the question where it belongs...
my goal is to change the constonant to the next consonant and the vowel to the next vowel for the arraynew_first = ["m", "a",
Adjusting for that here's some changes. You can unravel the deltas:
ALPHABET = ('a' .. 'z').to_a
VOWELS = %w[a e i o u]
CONSONANTS = ALPHABET - VOWELS # => ["b", "c", "d", "f", "g", "h", "j", "k", "l", "m", "n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z"]
SHIFTED_CONSONANTS = CONSONANTS.rotate # => ["c", "d", "f", "g", "h", "j", "k", "l", "m", "n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z", "b"]
SHIFTED_VOWELS = VOWELS.rotate # => ["e", "i", "o", "u", "a"]
SHIFTED_CONSONANTS_HASH = CONSONANTS.zip(SHIFTED_CONSONANTS).to_h # => {"b"=>"c", "c"=>"d", "d"=>"f", "f"=>"g", "g"=>"h", "h"=>"j", "j"=>"k", "k"=>"l", "l"=>"m", "m"=>"n", "n"=>"p", "p"=>"q", "q"=>"r", "r"=>"s", "s"=>"t", "t"=>"v", "v"=>"w", "w"=>"x", "x"=>"y", "y"=>"z", "z"=>"b"}
SHIFTED_VOWELS_HASH = VOWELS.zip(SHIFTED_VOWELS).to_h # => {"a"=>"e", "e"=>"i", "i"=>"o", "o"=>"u", "u"=>"a"}
sample_string = 'matthews'
sample_string.chars.map{ |c| SHIFTED_CONSONANTS_HASH[c] || SHIFTED_VOWELS_HASH[c] } # => ["n", "e", "v", "v", "j", "i", "x", "t"]
CONSONANTS_STR = CONSONANTS.join # => "bcdfghjklmnpqrstvwxyz"
SHIFTED_CONSONANTS_STR = SHIFTED_CONSONANTS.join # => "cdfghjklmnpqrstvwxyzb"
SHIFTED_VOWELS_STR = SHIFTED_VOWELS.join # => "eioua"
CHARACTERS_STR = (CONSONANTS + VOWELS).join # => "bcdfghjklmnpqrstvwxyzaeiou"
SHIFTED_CHARACTERS_STR = SHIFTED_CONSONANTS_STR + SHIFTED_VOWELS_STR # => "cdfghjklmnpqrstvwxyzbeioua"
sample_string.tr(CHARACTERS_STR, SHIFTED_CHARACTERS_STR).chars # => ["n", "e", "v", "v", "j", "i", "x", "t"]
The changes won't affect the speed of the actual code: tr
would still outrun using the hash lookups.
Upvotes: 0
Reputation: 211560
There's several ways to crack this nut, but it depends on your performance concerns, and how extensible this needs to be. Normally a chain of if
statements which are of the form x == y
and x == z
can be folded into:
case (x)
when y, z
# ... Executed on a match
end
In your case you can even do this by using your array as a list of valid values:
case (x)
when *constants
puts x.next!
end
For larger lists you might want to fold this up into a Set since these are optimized for include?
tests:
consonants_set = Set.new(consonants)
if (consonants_set.include?(x))
puts x.next!
end
Since you're doing single letter matches you have a lot more options. For example, a regular expression:
consonants_regexp = Regexp.new('[%s]' % consonants.join)
if (consonants_regexp.match(x))
puts x.next!
end
Or you can even do a simple substring match:
consonants_string = consonants.join
if (consonants_string[x])
puts x.next!
end
Worth noting but you can iterate over the characters in strings:
'cwzbrly'.each_char do |c|
puts c
end
That avoids the need to create and/or type in long arrays of the form [ 'a', 'b', ... ]
.
Upvotes: 4
Reputation: 29
If I correctly understand and you're expecting nuuixt
as a result, you can do:
new_first.select { |letter| consonants.include?(letter) && letter.next! }
&&
works here this way: if consonants.include?(letter)
evaluates to true then block return letter.next!
.
Upvotes: 0
Reputation: 10434
You can use regular expression
if x =~ /[aeiou]/
puts 'test failed'
else
puts x.next!
end
/[aeiou]/
is saying match anything that is a, e, i, o, or u.
This will eliminate the need to create an array of consonants.
Upvotes: 0
Reputation: 359
You can do this:
if consonants.include? (x)
your-code-here
end
This will check if there's an element equal to x inside the array.
Upvotes: 0