gsub - how to be efficient leet generator - ruby

for fun I am creating in ruby a simple leet (1337) generator
so i am doing something like this, which works but doesn't look very efficient, i am sure it can be accomplished with one line only...
def leet
words = words.gsub(/a/, '4')
words = words.gsub(/e/, '3')
words = words.gsub(/i/, '1')
words = words.gsub(/o/, '0')
words = words.gsub(/s/, '5')
words = words.gsub(/t/, '7')
puts words
end
Can you give me a help here? :) thanks!

def leet(word)
puts word.gsub(/[aeiost]/,'a'=>'4','e'=>'3','i'=>'1','o'=>'0','s'=>'5','t'=>'7')
end

def leet s
s.tr 'aeiost', '431057'
end

A more general version of megas's:
class Leet
##map = {
'a' => '4',
'e' => '3',
'i' => '1',
'o' => '0',
's' => '5',
't' => '7'
}
##re = Regexp.union(##map.keys)
def self.speak(str)
str.gsub(##re, ##map)
end
end
puts Leet.speak('leet')
# l337
Adjust ##map as needed and away you go.

Related

How to call a method from an another method with ruby?

I have this little password generating program, I want the method print_password to call the generate_password method, but it just doesn't work
require 'digest'
class Challenge
KEY = [
'4', '5', '6', '7',
'8', '9', 'A', 'B',
'C', 'D', 'E', 'F',
'0', '1', '2', '3'
]
def initialize(email)
#email = email # && raise
puts 'This object was initialized!'
end
def print_password
puts %(Password 1: {generate_password}) # Here I want to call generate_password method
end
private
def generate_password
#hash = Digest::SHA1.hexdigest(#email)
#id = #hash.scan(/\d+/).map(&:to_i).inject(:+)
#prng = Random.new(#id)
prepare_map
apply_map
end
def prepare_map
#map = []
id_string = #id.to_s
id_size = id_string.size
map_string = id_string * (KEY.size.to_f / id_size).ceil
0.upto(15) do |i|
#map[i] = map_string[i].to_i
end
#map
end
end
def apply_map
calculated_key = KEY.shuffle(random: #prng).map.with_index do |char, i|
(char.bytes[0] + #map[i]).chr
end
calculated_key.join
end
me = Challenge.new('me#gmail.com') # Initialize new object
me.print_password # Here I want to print the password
So here at the end, it initializes a new object and then where I use me.print_password it just prints out Password 1: {generate_password}
Don't know exactly what I am doing wrong here, thanks for your help in advance.
You need to use a hash character before your curly brackets (same notation as for double quotes):
puts %(Password: #{generate_password})
puts "Password: #{generate_password}"

Stop a loop if a certain character is passed to it Ruby

So I am working on a small assignment to transcribe DNA strands to RNA strands. My current code looks like this:
class Complement
def self.of_dna(str)
dna_rna = { 'G' => 'C', 'C' => 'G', 'T' => 'A', 'A' => 'U' }
rna = []
str.scan(/[GCTA]/).each do |x|
rna << dna_rna[x]
end
rna.join('')
end
end
It works perfectly, except for in one situation. If a DNA strand is passed that is partially correct, for example ACGTXXXCTTAA, my method will translate the DNA to RNA and just leave out the X's, giving me a result of UGCAGAAUU rather than just "". How can I make it so the loop will fail and exit when it receives a letter that isn't DNA related?
EDIT:
The test I am trying to get to pass looks like this:
def test_dna_correctly_handles_partially_invalid_input
# skip
assert_equal '', Complement.of_dna('ACGTXXXCTTAA')
end
I attempted #Holger Just's idea from below, and received this error:
1) Error:
ComplementTest#test_dna_correctly_handles_completely_invalid_input:
ArgumentError: ArgumentError
/Users/LukasBarry/exercism/ruby/rna-transcription/rna_transcription.rb:6:in `block in of_dna'
/Users/LukasBarry/exercism/ruby/rna-transcription/rna_transcription.rb:5:in `each'
/Users/LukasBarry/exercism/ruby/rna-transcription/rna_transcription.rb:5:in `of_dna'
rna_transcription_test.rb:43:in `test_dna_correctly_handles_completely_invalid_input'
The usual failure I've been getting from the above method is this:
1) Failure:
ComplementTest#test_dna_correctly_handles_partially_invalid_input [rna_transcription_test.rb:48]:
Expected: ""
Actual: "UGCAGAAUU"
Any help would be greatly appreciated.
Try this
class Complement
def self.of_dna(str)
return "" if str =~ /[^GCTA]/
...
end
end
Fun fact, you don't even need a loop to replace characters
str = 'GATTACA'
str.tr('ATCG', 'UAGC')
# => 'CUAAUGU'
Is all you need.
You can also match X in your regex and perform some erorr handling if it is found in the string. This could look something like this:
class Complement
def self.of_dna(str)
dna_rna = { 'G' => 'C', 'C' => 'G', 'T' => 'A', 'A' => 'U' }
rna = []
str.scan(/[GCTAX]/).each do |x|
return '' if x == 'X'
rna << dna_rna[x]
end
rna.join('')
end
end
I prefer using Hash#fetch for this because it'll raise a KeyError for you on mismatch, allowing you to write less code that validates inputs (i.e., less defensive programming), which I think is more valuable than cleverness (in which case I would recommend String#tr).
class DNA
TranslationMap = { 'G' => 'C', 'C' => 'G', 'T' => 'A', 'A' => 'U' }
attr_reader :dna
def initialize(dna)
#dna = dna
end
def to_rna
dna.each_char.map do |nucleotide|
TranslationMap.fetch(nucleotide)
end.join('')
rescue KeyError
return false
end
end
Feel free to adapt what happens when the error is rescued to fit your needs. I recommend raising a more specific exception (e.g. DNA::InvalidNucleotide) for the caller to handle.
In use:
dna = DNA.new 'GCTA'
# => #<DNA:0x007fc49e903818 #dna="GCTA">
dna.to_rna
# => "CGAU"
dna = DNA.new 'ACGTXXXCTTAA'
# => #<DNA:0x007fc49f064800 #dna="ACGTXXXCTTAA">
dna.to_rna
# => false

output to csv in a similar manner 'print' outputs to terminal?

the logic included in my ruby code is a little difficult to translate into a csv output, is there a way to append each value in a similar manner that print works when outputting to the terminal?
cop_log is a hash of thousands of records
cop_log = { 'A1' => 'val1', 'B1' => 'val2', 'C1' => 'val3', 'D1' => 'val4, 'E1' => 'val5', 'F1' => 'val6', 'G1' => 'val7', 'H1' => 'val8', 'I1' => 'val9', 'J1' => 'val10, 'K1' => 'val11', 'A2' => 'val12', 'B2' => 'val13', 'C2' => 'val14', 'D2' => 'val15, 'E2' => 'val16', 'F2' => 'val17', 'G2' => 'val18', 'H2' => 'val19', 'I2' => 'val20', 'J2' => 'val21, 'K2' => 'val22'}
cop_log.each do |key, value|
if key.include?('K')
print "#{value}\n"
elsif key.include?('A')
print "#{job_number},#{pm_lookup[job_number]},#{value},"
elsif key.include?('B') || key.include?('C') || key.include?('D') ||
key.include?('E') || key.include?('F') || key.include?('G') ||
key.include?('H') || key.include?('I') || key.include?('J')
print "#{value},"
end
end
currently outputs in the terminal like this, i want it to print to a csv in the same way:
val1, val2, val3, val4, val5, val6, val7, val8, val9, val10, val11
val12, val13, val14, val15, val16, val17, val18, val19, val10, val21, val22
reading the documentation it looks like the common route is to do the following,
CSV.open("path/to/file.csv", "wb") do |csv|
csv << ["row", "of", "CSV", "data"]
csv << ["another", "row"]
# ...
end
unfortunately the structure of my code is going to make that a little difficult....
This is very confusing and, if cleaned up, would help make your code much more readable and maintainable:
if key.include?('K')
print "#{value}\n"
elsif key.include?('A')
print "#{job_number},#{pm_lookup[job_number]},#{value},"
elsif key.include?('B') || key.include?('C') || key.include?('D') ||
key.include?('E') || key.include?('F') || key.include?('G') ||
key.include?('H') || key.include?('I') || key.include?('J')
print "#{value},"
end
What is key? A string of characters or a single character? If you're matching single characters you could do something like:
if key == 'K'
print "#{value}\n"
elsif key == 'A'
print "#{job_number},#{pm_lookup[job_number]},#{value},"
elsif ('B'.. 'J').include?(key)
elsif
print "#{value},"
end
and even convert:
elsif ('B'.. 'J').include?(key)
to:
elsif ('B'..'J') === key
If you're searching for a character match inside strings:
if key['K']
print "#{value}\n"
elsif key['A']
print "#{job_number},#{pm_lookup[job_number]},#{value},"
elsif key[/[B-J]/]
print "#{value},"
end
At this point you're ready to tackle cleaning up the CSV output. I'd recommend starting over using one of the basic examples of CSV output in the class documentation. I'd also recommend carefully reading "Comma-separated values" so you're more familiar with the expectations of a CSV formatted file.
(Note: At this point the input sample hash was added to the question showing the actual format of the keys.)
I'll let you figure out how to incorporate CSV, but meditate on this:
cop_log = { 'A1' => 'val1', 'B1' => 'val2', 'C1' => 'val3', 'D1' => 'val4', 'E1' => 'val5', 'F1' => 'val6', 'G1' => 'val7', 'H1' => 'val8', 'I1' => 'val9', 'J1' => 'val10', 'K1' => 'val11', 'A2' => 'val12', 'B2' => 'val13', 'C2' => 'val14', 'D2' => 'val15', 'E2' => 'val16', 'F2' => 'val17', 'G2' => 'val18', 'H2' => 'val19', 'I2' => 'val20', 'J2' => 'val21', 'K2' => 'val22'}
cop_log.values.each_slice(11) do |slice|
puts slice.join(',')
end
# >> val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,val11
# >> val12,val13,val14,val15,val16,val17,val18,val19,val20,val21,val22
You need to rely on the CSV class to create CSV files; The format isn't as simple as it seems.
Here's a first pass of how I'd write the code:
require 'csv'
cop_log = { 'A1' => 'val1', 'B1' => 'val2', 'C1' => 'val3', 'D1' => 'val4', 'E1' => 'val5', 'F1' => 'val6', 'G1' => 'val7', 'H1' => 'val8', 'I1' => 'val9', 'J1' => 'val10', 'K1' => 'val11', 'A2' => 'val12', 'B2' => 'val13', 'C2' => 'val14', 'D2' => 'val15', 'E2' => 'val16', 'F2' => 'val17', 'G2' => 'val18', 'H2' => 'val19', 'I2' => 'val20', 'J2' => 'val21', 'K2' => 'val22'}
job_number = 1
pm_lookup = [0, 1]
CSV.open('./foo.csv', 'w') do |csv|
cop_log.group_by{ |k, v| k[1] }.values.each do |row|
csv << [ job_number, pm_lookup[job_number], *row.map{ |k, v| v } ]
end
end
After running, foo.csv contains:
1,1,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,val11
1,1,val12,val13,val14,val15,val16,val17,val18,val19,val20,val21,val22
We don't know what your expected output is, since you didn't tell us, but you could work from that code and figure it out.
Here's a break-down of the preprocessing of the data:
cop_log.group_by{ |k, v| k[1] } # => {"1"=>[["A1", "val1"], ["B1", "val2"], ["C1", "val3"], ["D1", "val4"], ["E1", "val5"], ["F1", "val6"], ["G1", "val7"], ["H1", "val8"], ["I1", "val9"], ["J1", "val10"], ["K1", "val11"]], "2"=>[["A2", "val12"], ["B2", "val13"], ["C2", "val14"], ["D2", "val15"], ["E2", "val16"], ["F2", "val17"], ["G2", "val18"], ["H2", "val19"], ["I2", "val20"], ["J2", "val21"], ["K2", "val22"]]}
.values # => [[["A1", "val1"], ["B1", "val2"], ["C1", "val3"], ["D1", "val4"], ["E1", "val5"], ["F1", "val6"], ["G1", "val7"], ["H1", "val8"], ["I1", "val9"], ["J1", "val10"], ["K1", "val11"]], [["A2", "val12"], ["B2", "val13"], ["C2", "val14"], ["D2", "val15"], ["E2", "val16"], ["F2", "val17"], ["G2", "val18"], ["H2", "val19"], ["I2", "val20"], ["J2", "val21"], ["K2", "val22"]]]
In the code above I'm making sure the input is in an expected order, in this case I'm grouping by single-digit rows. More robust code would use \d+ instead of 1 and would also sort to force the appropriate order.
This is really important any time you're massaging data from one format into another for later reuse. While current Rubies guarantee that a hash will maintain its insertion order, it's not a good practice to assume since old versions of Ruby didn't do that, and other languages you port the code to might not maintain order. Instead always program defensively, making sure you're going to return consistent results. To what extent you do that is something you'll learn when you find out a previous attempt wasn't enough. Programming is fun that way.
Finally, any time your code feels awkward or isn't flowing it's time to back up and look at what you're doing.
Couldn't you do this?
CSV.open("path/to/file.csv", "wb") do |csv|
cop_log.each do |key, value|
if key.include?('A')
csv << [ job_number, pm_lookup[job_number], value ]
elsif key.include?('K') || key.include?('B') || key.include?('C') || key.include?('D') ||
key.include?('E') || key.include?('F') || key.include?('G') ||
key.include?('H') || key.include?('I') || key.include?('J')
csv << [ value ]
end
end
end
def write_csv(cop_log, io = $stdout)
cop_log.each do |key, value|
if key.include?('K')
io.print "#{value}\n"
elsif key.include?('A')
io.print "#{job_number},#{pm_lookup[job_number]},#{value},"
elsif key.include?('B') || key.include?('C') || key.include?('D') ||
key.include?('E') || key.include?('F') || key.include?('G') ||
key.include?('H') || key.include?('I') || key.include?('J')
io.print "#{value},"
end
end
end
Then
File.open('path/to/file.csv', 'w') do |f|
write_csv(cop_log, f)
end
re fector the code
CSV.open("test.csv", "ab") do |csv|
b = {}
check_num = 0
cop_log.each do |key, value|
num = key.gsub(/[^\d]/, '').to_i
next if num < 8 ||
key.include?('L') || key.include?('M') || key.include?('N') ||
key.include?('O') || key.include?('P') || key.include?('Q') ||
key.include?('R') || key.include?('S') || key.include?('T') ||
key.include?('U') || key.include?('V')
a = { key => value }
b.merge!(a)
end # end of each loop
i = 8
while ((b.length / 9) - 7) > i do
csv << [ job_number, pm_lookup[job_number], b["A#{i}"], b["B#{i}"],
b["C#{i}"], b["D#{i}"], b["E#{i}"], b["F#{i}"], b["G#{i}"],
b["H#{i}"], b["I#{i}"], b["J#{i}"], b["K#{i}"] ]
i += 1
end
end # end of CSV.open

Advancing Vowel to the Next Vowel in Ruby

I'm working on beginner Ruby tutorials. I'm trying to write a method that will advance vowels to the next vowel. 'a' will become 'e', 'e' will become 'i', 'u' will become 'a', etc etc. I've been trying various ideas for quite a while, to no avail.
I think I'm on the right track in that I need to create an array of the vowels, and then use an index to advance them to the next array in the vowel. I just can't seem to create the right method to do so.
I know this isn't workable code, but my outline is along these lines. Where I run into issues is getting my code to recognize each vowel, and advance it to the next vowel:
def vowel_adv(str)
vowels = ["a", "e", "i", "o", "u"]
str = str.split('')
**str_new = str.map do |letter|
if str_new.include?(letter)
str_new = str_new[+1]
end**
# The ** section is what I know I need to find working code with, but keep hitting a wall.
str_new.join
end
Any help would be greatly appreciated.
Because we have only a few vowels, I would first define a hash containing vowel keys and vowel values:
vowels_hash = {
'a' => 'e',
'A' => 'E',
'e' => 'i',
'E' => 'I',
'i' => 'o',
'I' => 'O',
'o' => 'u',
'O' => 'U',
'u' => 'a',
'U' => 'A'
}
Then I would iterate over the alphabets present in each word / sentence like so:
word.split(//).map do |character|
vowels_hash[character] || character
end.join
Update:
BTW instead of splitting the word, you could also use gsub with regex + hash arguments like so:
word.gsub(/[aeiouAEIOU]/, vowels_hash)
Or like so if you want to be Mr. / Ms. Fancy Pants:
regex = /[#{vowels_hash.keys.join}]/
word.gsub(regex, vowels_hash)
Here's your code with the fewest corrections necessary to make it work:
def vowel_adv(str)
vowels = ["a", "e", "i", "o", "u"]
str = str.split('')
str_new = str.map do |char|
if vowels.include?(char)
vowels.rotate(1)[vowels.index(char)]
else
char
end
end
str_new.join
end
vowel_adv "aeiou"
=> "eioua"
Things that I changed include
addition of a block variable to the map block
returning the thing you're mapping to from the map block
include? is called on the Array, not on the possible element
finding the next vowel by looking in the array of vowels, not by incrementing the character, which is what I think you were trying to do.
Here's an improved version:
VOWELS = %w(a e i o u)
ROTATED_VOWELS = VOWELS.rotate 1
def vowel_adv(str)
str.
chars.
map do |char|
index = VOWELS.index char
if index
ROTATED_VOWELS[index]
else
char
end
end.
join
end
static Arrays in constants
nicer array-of-string syntax
String#chars instead of split
use the index to test for inclusion instead of include?
no assignment to parameters, which is a little confusing
no temporary variables, which some people like and some people don't but I've done here to show that it's possible
And, just because Ruby is fun, here's a different version which copies the string and modifies the copy:
VOWELS = %w(a e i o u)
ROTATED_VOWELS = VOWELS.rotate 1
def vowel_adv(str)
new_str = str.dup
new_str.each_char.with_index do |char, i|
index = VOWELS.index char
if index
new_str[i] = ROTATED_VOWELS[index]
end
end
new_str
end
The string class has a nice method for this. Demo:
p "Oh, just a test".tr("aeiouAEIOU", "uaeioUAEIO") # => "Ih, jost u tast"
To riff of of steenslag's answer a little.
VOWELS = %w{a e i o u A E I O U}
SHIFTED_VOWELS = VOWELS.rotate 1
def vowel_shifter input_string
input_string.tr!(VOWELS.join, SHIFTED_VOWELS.join)
end
and just for fun, consonants:
CONSONANTS = ('a'..'z').to_a + ('A'..'Z').to_a - VOWELS
SHIFTED_CONSONANTS = CONSONANTS.rotate 1
def consonant_shifter input_string
input_string.tr!(CONSONANTS.join, SHIFTED_CONSONANTS.join)
end

How to read characters from a text file, then store them into a hash in Ruby

I am working on an assignment, and can't figure it out. We have to first parse a text file, and then feed the results into a hash. I have done this:
code = File.open(WORKING_DIR + '/code.txt','r')
char_count = {'a' => 0,'b' => 0,'c' => 0,'d' => 0,'e' => 0,'f' => 0,'g' => 0,'h' => 0,'i' => 0,
'j' => 0,'k' => 0,'l' => 0,'m' => 0,'n' => 0,'o' => 0,'p' => 0,'q' => 0,'r' => 0,
's' => 0,'t' => 0,'u' => 0,'v' => 0,'w' => 0,'x' => 0,'y' => 0,'z' => 0
}
# Step through each line in the file.
code.readlines.each do |line|
# Print each character of this particular line.
line.split('').each do
|ch|
char_count.has_key?('ch')
char_count['ch'] +=1
end
My line of thinking: open the file to a variable named code
read the individual lines
break the lines into each character.
I know this works, I can puts out the characters to screen.
Now I need to feed the characters into the hash, and it isn't working. I am struggling with the syntax (at least) and basic concepts (at most). I only want the alphabet characters, not the punctuation, etc. from the file.
Any help would be greatly appreciated.
Thanks.
I would directly do :
File.open(WORKING_DIR + '/code.txt','r') do |f|
char_count = Hash.new(0) # create a hash where 0 is the default value
f.each_char do |c| # iterate on each character
... # some filter on the character you want to reject.
char_count[c] +=1
end
end
PS : you wrote 'ch' the string instead of ch the variable name
EDIT : the filter could be
f.each_char do |c| # iterate on each character
next if c ~= \/W\ # exclude with a regexp non word character
....
Try this, using Enumerable class methods:
open("file").each_char.grep(/\w/).group_by { |char|
char
}.each { |char,num|
p [char, num.count]
}
(The grep method filter is using regex "\w" (any character, digit ou underscore); you can change to [A-Za-z] for filter only alphabets.)
I think the problem is here:
char_count.has_key?('ch')
char_count['ch'] +=1
end
You're not using the variable but a string 'ch', change that in both places for ch.
Also the hash could be created using range, for example:
char_count = {}
('a'..'z').each{|l| char_count[l] = 0}
or:
char_count = ('a'..'z').inject({}){|hash,l| hash[l] = 0 ; hash}

Resources