Replace the pipe character “|” with line blank space - ruby

I'm trying to solve a code challenge about Morse Code, the idea is to:
Receive: morse_text = '.... ..|--. ..- -.-- ...'
Return: 'HI GUYS'
But I am getting 'HIGUYS'
where the pipe should be converted into a space between the 2 words. So far I got:
def decode(morse_text)
# TODO: Decode the morse text
morse_text = morse_text.tr("|", " ")
array = morse_text.split(" ").map { |word| encode_word.invert[word].upcase }
array.join
end
def encode_word
morse_code = {
"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" => "--..",
" " => "|"
}
end
I'm struggling in convert the pipe into a blank space so I can get the desire result.

The issue is that you're converting the pipe to a space which means you lose the unique separator for words and treat it as just a standard separator of characters. Instead, split by the pipe and operate on an array of words:
def decode(morse_text)
# Split the morse code into an array of encoded words
encoded_words = morse_text.split('|')
# Decode each word letter by letter
decoded_words = encoded_words.map do |word|
word.split(' ').map { |letter| encode_word.invert[letter].upcase }
end
# Join each decoded word into a string
joined_words = decoded_words.map { |word| word.join }
# Join each word into a single string
decoded_text = joined_words.join(' ')
end
The result is:
decode('.... ..|--. ..- -.-- ...')
=> "HI GUYS"

Simply use the form of String#gsub that employs a hash for making substitutions.
If the variable morse_code holds your hash, with the additional key_value pair ""=>" ", compute the following hash.
decoding_map = morse_code.invert.transform_values(&:upcase)
#=> {".-"=>"A", "-..."=>"B", "-.-."=>"C", "-.."=>"D", "."=>"E",
# ...
# "-..-"=>"X", "-.--"=>"Y", "--.."=>"Z", "|"=>" ", , " "=>""}
Then
morse_text = '.... ..|--. ..- -.-- ...'
morse_text.gsub(/[| ]|[.-]+/, decoding_map)
#=> "HI GUYS"
The regular expression reads, "match a pipe or space or a mix of one or more periods or hyphens".

Related

How do you access two methods from within another method?

I am creating a caesar cipher for The Odin Project's Ruby Programming course, I have my code to the point where I have one method that can take a single word and a shift value and returns the ciphered word using corresponding hash keys and values. And I have another method that takes a sentence and splits it into an array containing each separated word. What I would like to do is combine these two methods so that when you input a sentence, the words are split up into an array, then each part of the array is ciphered using the shift value, then the ciphered words from the array are printed back into sentence form.
Here is my code so far:
"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,
}```
```#multi letter caesar_cipher
def word_cipher(word, shift)
word.split(//).each {|letter| print #cipher.key(#cipher[letter]+ shift)}
end
> word_cipher("kittens", 2)
=> mkvvgpu
#split sentence string into an array of words
def sentence_array(sentence)
sentence.split.each { |word| print word.split }
end
>sentence_array("Look at all of these kittens")
=>["Look"]["at"]["all"]["of"]["these"]["kittens"]
And what I have for my the solution so far
def caesar_cipher(input, shift)
sentence_array(input) = words
words.split(//).each {|letter| print #cipher.key(#cipher[letter]+ shift)}
end
caesar_cipher("I love kittens", 2)
This is my first time posting on here so I'm sorry if I did a bad job explaining anything but any help would be appreciated!!
Thanks!
you have to slightly modify the methods:
#multi letter caesar_cipher
def word_cipher(word, shift)
output = ''
word.split(//).each {|letter| output << #cipher.key(#cipher[letter]+ shift)}
output
end
def sentence_array(sentence)
sentence.split
end
#multi letter caesar_cipher
def caesar_cipher(input, shift)
output = ""
words = sentence_array(input)
words.each do |word|
output << word_cipher(word.downcase, shift)
output << " " unless word == words.last
end
output.capitalize
end
puts caesar_cipher("I love kittens", 2)

gsub method and regex (case sensitive and case insensitive)

In ruby, I want to substitute some letters in a string, is there a better way of doing this?
string = "my random string"
string.gsub(/a/, "#").gsub(/i/, "1").gsub(/o/, "0")`
And if I want to substitute both "a" and "A" with a "#", I know I can do .gsub(/a/i, "#"), but what if I want to substitute every "a" with an "e" and every "A" with an "E"? Is there a way of abstracting it instead of specifying both like .gsub(/a/, "e").gsub(/A/, "E")?
You can use a Hash. eg:
h = {'a' => '#', 'b' => 'B', 'A' => 'E'}
"aAbBcC".gsub(/[abA]/, h)
# => "#EBBcC"
Not really an answer to your question, but more an other way to proceed: use the translation:
'aaAA'.tr('aA', 'eE')
# => 'eeEE'
For the same transformation, you can also use the ascii table:
'aaAA'.gsub(/a/i) {|c| (c.ord + 4).chr}
# => 'eeEE'
other example (the last character is used by default):
'aAaabbXXX'.tr('baA', 'B#')
# => '####BBXXX'
Here are two variants of #Santosh's answer:
str ="aAbBcC"
h = {'a' => '#', 'b' => 'B', 'A' => 'E'}
#1
str.gsub(/[#{h.keys.join}]/, h) #=> "#EBBcC"
#2
h.default_proc = ->(_,k) { k }
str.gsub(/./, h) #=> "#EBBcC"
These offer better maintainability should h could change in future
You can also pass gsub a block
"my random string".gsub(/[aoi]/) do |match|
case match; when "a"; "#"; when "o"; "0"; when "i"; "I" end
end
# => "my r#nd0m strIng"
The use of a hash is of course much more elegant in this case, but if you have complex rules of substitution it can come in handy to dedicate a class to it.
"my random string".gsub(/[aoi]/) {|match| Substitute.new(match).result}
# => "my raws0m strAINng"

Get first index of any character among array from a string in Ruby?

I have string like this
hi, i am not coming today!
and i have an array of characters like this:
['a','e','i','o','u']
now i want to find the first occurrence of any word from array in string.
If it was only word i'd have been able to do it like this:
'string'.index 'c'
s = 'hi, i am not coming today!'
['a','e','i','o','u'].map { |c| [c, s.index(c)] }.to_h
#⇒ {
# "a" => 6,
# "e" => nil,
# "i" => 1,
# "o" => 10,
# "u" => nil
# }
To find the first occurence of any character from an array:
['a','e','i','o','u'].map { |c| s.index(c) }.compact.min
#⇒ 1
UPD Something different:
idx = str.split('').each_with_index do |c, i|
break i if ['a','e','i','o','u'].include? c
end
idx.is_a?(Numeric) ? idx : nil
str =~ /#{['a','e','i','o','u'].join('|')}/
str.index Regexp.union(['a','e','i','o','u']) # credits #steenslag

How to break a string into two arrays in Ruby

Is there a way to extract the strings removed by String#split into a separate array?
s = "This is a simple, uncomplicated sentence."
a = s.split( /,|\./ ) #=> [ "This is a simple", "uncomplicated sentence" ]
x = ... => should contain [ ",", "." ]
Note that the actual regex I need to use is much more complex than this example.
Something like this ?
a = s.scan( /,|\./ )
When you want both the matched delimiters and the substrings in between as in Stefan's comment, then you should use split with captures.
"This is a simple, uncomplicated sentence."
.split(/([,.])/)
# => ["This is a simple", ",", " uncomplicated sentence", "."]
If you want to separate them into different arrays, then do:
a, x =
"This is a simple, uncomplicated sentence."
.split(/([,.])/).each_slice(2).to_a.transpose
a # => ["This is a simple", " uncomplicated sentence"]
x # => [",", "."]
or
a =
"This is a simple, uncomplicated sentence."
.split(/([,.])/)
a.select.with_index{|_, i| i.even?}
# => ["This is a simple", " uncomplicated sentence"]
a.select.with_index{|_, i| i.odd?}
# => [",", "."]
try this:
a = s.split(/,/)[1..-1]

Why are my Ruby methods returning the same thing?

The question is related to Morse code:
# Build a function, `morse_encode(str)` that takes in a string (no
# numbers or punctuation) and outputs the morse code for it. See
# http://en.wikipedia.org/wiki/Morse_code. Put two spaces between
# words and one space between letters.
#
# You'll have to type in morse code: I'd use a hash to map letters to
# codes. Don't worry about numbers.
#
# I wrote a helper method `morse_encode_word(word)` that handled a
# single word.
#
# Difficulty: 2/5
describe "#morse_encode" do
it "should do a simple letter" do
morse_encode("q").should == "--.-"
end
it "should handle a small word" do
morse_encode("cat").should == "-.-. .- -"
end
it "should handle a phrase" do
morse_encode("cat in hat").should == "-.-. .- - .. -. .... .- -"
end
end
My solution is
MORSE_CODE = {
"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" => "--.."
}
def morse_encode(str)
arrayer = str.split(" ")
combiner = arrayer.map {|word| morse_encode_word(word) }
combiner.join(" ")
end
def morse_encode_word(word)
letters = word.split("")
array = letters.map {|x| MORSE_CODE[x]}
array.join(" ")
end
morse_encode("cat in hat")
morse_encode_word("cat in hat")
Why are morse_encode and morse_encode_word returning the exact same output?
The way I created it, I would think there would be spacing differences.
When you pass a phrase into morse_encode_word, it splits it by letters (that is, 't i' becomes ['t', ' ', 'i']. Next, you map this array to ['-', nil, '..'] (because MORSE_CODE[' '] == nil).
And then you join it with space, '-' + ' ' + '' + ' ' + '..' (because nil.to_s == ''). So you get string with two spaces inside, '- ..'.
When you do morse_encode_word you are not getting rid of spaces... So it will split your words, but keep the space.
In morse_encode you get rid of the space (because of split), but you add it back in when you do the join. So it ends up being the same as morse_encode_word.
I would guess that what you want is no extra spaces in morse_encode_word. Just do a check to make sure x is not a space before you map it in morse_encode_word.
Try to use reject:
def morse_encode_word(word)
letters = word.split("")
array = letters.reject{|x| x == " "}.map{|x| MORSE_CODE[x]}
array.join(" ")
end

Resources