Reputation: 23
I want to take a word, for example "Mathew", and turn it into "Nevjix" (but it should work for any word). The vowels cycle to the next vowel, and the consonants cycle to the next consonant.
I created two arrays, for vowels and consonants, and the code correctly identifies which is which, by displaying either the words "vowel" or "consonant".
I think I need to add something like a next!
to the if
statement, but I'm really not sure.
Here's what I have:
p "Input Agent Name"
name = gets.chomp
name = "#{name}".chars
array1 = ['a', 'e', 'i', 'o', 'u']
array2 = ['b', 'c', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'm', 'n', 'p', 'q', 'r', 's', 't', 'v', 'w', 'x', 'y', 'z']
p name
value = -1
until value == name.length
value = value+1
if array1.include? name[value]
p "vowel"
elsif array2.include? name[value]
p "consonant"
end
end
p name
Upvotes: 0
Views: 60
Reputation: 160631
Here's how I'd go about it:
VOWELS = %w[a e i o u]
CONSONANTS = ('a' .. 'z').to_a - VOWELS
# => ["b", "c", "d", "f", "g", "h", "j", "k", "l", "m", "n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z"]
CHAR_MAP = ((VOWELS + CONSONANTS).zip(VOWELS.rotate + CONSONANTS.rotate)).to_h
# => {"a"=>"e", "e"=>"i", "i"=>"o", "o"=>"u", "u"=>"a", "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"}
CHAR_RE = Regexp.union(CHAR_MAP.keys)
# => /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/
'matthew'.gsub(CHAR_RE, CHAR_MAP) # => "nevvjix"
gsub
makes it really easy to map words, substrings, individual characters, depending on the regular expression used. I let Regexp.union
build the pattern, but it could have also been:
CHAR_RE = /[#{CHAR_MAP.keys.join}]/
# => /[aeioubcdfghjklmnpqrstvwxyz]/
or:
CHAR_RE = /[#{(VOWELS + CONSONANTS).join}]/
# => /[aeioubcdfghjklmnpqrstvwxyz]/
or, even better in this particular situation:
CHAR_RE = /./
which would return the same results.
There's a tiny build-up time to create the arrays, map and regular expression, so I'd create them as constants then reuse them throughout any code.
To handle upper-case characters, tweak it a bit:
LC_VOWELS = %w[a e i o u]
LC_CONSONANTS = ('a' .. 'z').to_a - LC_VOWELS
UC_VOWELS, UC_CONSONANTS = [LC_VOWELS, LC_CONSONANTS].map{ |a| a.map(&:upcase) }
CHAR_MAP = (
(LC_VOWELS + UC_VOWELS + LC_CONSONANTS + UC_CONSONANTS).zip(
[
LC_VOWELS,
UC_VOWELS,
LC_CONSONANTS,
UC_CONSONANTS
].map(&:rotate).inject(:+)
)
).to_h
# => {"a"=>"e", "e"=>"i", "i"=>"o", "o"=>"u", "u"=>"a", "A"=>"E", "E"=>"I", "I"=>"O", "O"=>"U", "U"=>"A", "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", "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"}
CHAR_RE = /./
'Matthew'.gsub(CHAR_RE, CHAR_MAP) # => "Nevvjix"
For this problem tr
would also do it, but it's only applicable when changing/translating individual characters. It lacks the ability to use a regular expression, so, while it's faster, it's less flexible.
Upvotes: 1
Reputation: 2302
String#tr
could be used to achieve this. I'm not sure if you want to preserve casing (in which case you could either expand the two sets of strings or add some logic).
'matthew'.tr('bcdfghjklmnpqrstvwxyzaeiou', 'cdfghjklmnpqrstvwxyzbeioua')
#=>"nevvjix"
Alternatively to preserve casing you could write it as such
puts 'Input Agent Name'
name = gets.chomp.chars
def transform_character(char)
char.tr('bcdfghjklmnpqrstvwxyzaeiou', 'cdfghjklmnpqrstvwxyzbeioua')
end
name.map! do |char|
if char == char.upcase
transform_character(char.downcase).upcase
else
transform_character(char)
end
end
puts name.join
I've opted to use a simple if/else rather than the ternary operator (below) for clarity.
name.map! do |char|
char == char.upcase ? transform_character(char.downcase).upcase : transform_character(char)
end
Upvotes: 2
Reputation: 8995
You can probably simplify your solution by realizing that "a consonant" is ... "not-a-vowel." (You don't actually need to maintain two different lists ...) The second elsif
is unnecessary, because, "if it is not 'one,' it must be 'the other.'"
Upvotes: 0
Reputation: 11038
You should use a single map from the original letter to the re-mapped letter. And use map
to iterate over the input string:
lookup = { 'a' => 'e', 'b' => 'c', 'c' => 'd', ..... }
name.chars.map! {|c| lookup[c]}
Upvotes: 1