How do you access two methods from within another method? - ruby

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)

Related

Replace the pipe character “|” with line blank space

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".

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

Why does the addition operator append hash key-value pairs?

I'm new to programming and trying to make sense of why an operator almost always use for addition, appends a hash key-value pair in Ruby.
The following code snippet is from the Pragmatic Studio Ruby course:
letters = {"c" => 3, "e" => 1, "l" => 1, "n" => 1, "t" => 1, "x" => 8, "y" => 4}
point_totals = Hash.new(0)
"excellently".each_char do |char|
point_totals[char] += letters[char]
end
puts point_totals
puts point_totals.values.reduce(0, :+)
Output
{"e"=>3, "x"=>8, "c"=>3, "l"=>3, "n"=>1, "t"=>1, "y"=>4}
23
Why does the language use += instead of <<?
You have += because you are adding the value of letters[char] to points_total[char]
<< is used for appending to an array.
You're not appending a key-value pair, you're incrementing the value associated with the char key in the points_total hash.

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

Anagrams Code Kata, Ruby Solution very slow

I've been having a play with Ruby recently and I've just completed the Anagrams Code Kata from http://codekata.pragprog.com.
The solution was test driven and utilises the unique prime factorisation theorem, however it seems to run incredibly slow. Just on the 45k file it's been running for about 10 minutes so far. Can anyone give me any pointers on improving the performance of my code?
class AnagramFinder
def initialize
#words = self.LoadWordsFromFile("dict45k.txt")
end
def OutputAnagrams
hash = self.CalculatePrimeValueHash
#words.each_index{|i|
word = #words[i]
wordvalue = hash[i]
matches = hash.select{|key,value| value == wordvalue}
if(matches.length > 1)
puts("--------------")
matches.each{|key,value|
puts(#words[key])
}
end
}
end
def CalculatePrimeValueHash
hash = Hash.new
#words.each_index{|i|
word = #words[i]
value = self.CalculatePrimeWordValue(word)
hash[i] = value
}
hash
end
def CalculatePrimeWordValue(word)
total = 1
hash = self.GetPrimeAlphabetHash
word.downcase.each_char {|c|
value = hash[c]
total = total * value
}
total
end
def LoadWordsFromFile(filename)
contentsArray = []
f = File.open(filename)
f.each_line {|line|
line = line.gsub(/[^a-z]/i, '')
contentsArray.push line
}
contentsArray
end
def GetPrimeAlphabetHash
hash = { "a" => 2, "b" => 3, "c" => 5, "d" => 7, "e" => 11, "f" => 13, "g" =>17, "h" =>19, "i" => 23, "j" => 29, "k" => 31, "l" => 37, "m" => 41, "n" =>43, "o" =>47, "p" => 53, "q" =>59, "r" => 61, "s" => 67, "t" => 71, "u" => 73, "v" => 79, "w" => 83, "x" => 89, "y" => 97, "z" => 101 }
end
end
Frederick Cheung has a few good points, but I thought I might provide you with a few descriptive examples.
I think your main problem is that you create your index in a way that forces you to do linear searches in it.
Your word list (#words) seems to look something like this:
[
"ink",
"foo",
"kin"
]
That is, it is just an array of words.
Then you create your hash index with CalculatePrimeValueHash, with hash keys being equal to the word's index in #words.
{
0 => 30659, # 23 * 43 * 31, matching "ink"
1 => 28717, # 13 * 47 * 47, matching "foo"
2 => 30659 # 31 * 23 * 43, matching "kin"
}
I would consider this a good start, but the thing is if you keep it like this, you will have to iterate through the hash to find what hash keys (i.e. indexes in #words) that belong together, and then iterate through those to join them. That is, the basic problem here is that you do things too granularly.
If you instead were to build this hash with the prime values as hash keys, and have them point to an array of the words with that key, you would get a hash index like this instead:
{
30659 => ["ink", "kin"],
28717 => ["foo"]
}
With this kind of structure, the only thing you have to do to write your output, is to just iterate over the hash values and print them, since they are already grouped.
Another thing with your code, is that it seems to generate a whole bunch of throwaway objects , which will make sure to keep your garbarge collector busy, and that is generally quite a big choke point in ruby.
It might also be a good thing to go find either a benchmark tool and/or a profiler to analyze your code and see where it could be approved upon.
Fundamentally your code is slow because for each word (45k) of them you iterate over the entire hash (45k of them) looking for words with the same signature, so you're doing 45k * 45k of these comparisons. Another way of phrasing that is to say that your complexity is n^2 in the number of words.
The code below implements your basic idea but runs in a few seconds on the 236k word file I happen to have lying around. It could definitely be faster - the second pass over the data to find the things with > 1 items could be eliminated but would be less readable
It's also a lot shorter than your code, around a third, while staying readable, largely because I used more standard library functions and idiomatic ruby.
For example, the load_words method uses collect to turn one array into another, rather than iterating over one array and adding things to a second one. Similarly the signature function uses inject rather than iterating over the characters. Lastly I've used group_by to do the actual grouping. All of these methods happen to be in Enumerable - it's well worth becoming very familiar with these.
signature_for_word could become even pithier with
word.each_char.map {|c| CHAR_MAP[c.downcase]}.reduce(:*)
This takes the word, splits it into characters and then maps each one of those to the right number. reduce(:*) (reduce is an alias for inject) then multiplies them all together.
class AnagramFinder
CHAR_MAP ={ "a" => 2, "b" => 3, "c" => 5, "d" => 7, "e" => 11, "f" => 13, "g" =>17, "h" =>19, "i" => 23, "j" => 29, "k" => 31, "l" => 37, "m" => 41, "n" =>43, "o" =>47, "p" => 53, "q" =>59, "r" => 61, "s" => 67, "t" => 71, "u" => 73, "v" => 79, "w" => 83, "x" => 89, "y" => 97, "z" => 101 }
def initialize
#words = load_words("/usr/share/dict/words")
end
def find_anagrams
words_by_signature = #words.group_by {|word| signature_for_word word}
words_by_signature.each do |signaure, words|
if words.length > 1
puts '----'
puts words.join('; ')
end
end
end
def signature_for_word(word)
word.downcase.each_char.inject(1) {| total, c| total * CHAR_MAP[c]}
end
def load_words(filename)
File.readlines(filename).collect {|line| line.gsub(/[^a-z]/i, '')}
end
end
You can start limiting the slowness by using the Benchmark tool. Some examples here:
http://www.skorks.com/2010/03/timing-ruby-code-it-is-easy-with-benchmark/
First of all it would be interesting to see how long it takes to run self.calculate_prime_value_hash and after that the calculate_prime_word_value.
Quite often the slowness boils down to the number of times the inners loops are run so you can also log how many times they are run.
One very quick improvement you can do is to set the prime alhabet hash as a constant because it's not changed at all:
PRIME_ALPHABET_HASH = { "a" => 2, "b" => 3, "c" => 5, "d" => 7, "e" => 11, "f" => 13, "g" =>17, "h" =>19, "i" => 23, "j" => 29, "k" => 31, "l" => 37, "m" => 41, "n" =>43, "o" =>47, "p" => 53, "q" =>59, "r" => 61, "s" => 67, "t" => 71, "u" => 73, "v" => 79, "w" => 83, "x" => 89, "y" => 97, "z" => 101 }

Resources