Replace different sets of characters with different sets of numbers using regex - ruby

I am trying to replace characters in a string with a shift in the ord by some number. I am thinking the best way to do this is with regex, but running into some problems.
This is the flawed code I do have
def cipher(coded_message)
coded_message=coded_message.downcase.split("")
new_message=[]
coded_message.each do |x|
x=x.gsub(/[a-d][e-z]/, '\1x.ord+22\2x.ord-4')
new_message<<x
end
p new_message.join
end
I know that my problem is with the regex and probably the replacement text, but not sure where to go on this one. Any help would be appreciated.

Ok so I took a different approach to solving your problem. Here is a solution which doesn't involve a regex, and is very flexible.
def cipher(coded_message)
new_message=[]
coded_message.downcase.each_char do |x|
case x
when ("a".."d")
new_message << (x.ord+22).chr
when ("e".."z")
new_message << (x.ord-4).chr
end
end
new_message.join
end
cipher("Code this string")
#=> "ykzapdeoopnejc"

Not much point coding a message if you can't decode it:
#code_key = 123.times.with_object({}) do |i,h|
c = i.chr
h[c] =
case c
when /[a-dA-D]/
(i+22).chr
when /[e-zE-Z]/
(i-4).chr
else
c
end
end
#decode_key = #code_key.invert
def code(message)
#code_key.values_at(*message.chars).join
end
def decode(message)
#decode_key.values_at(*message.chars).join
end
message = "Is 42 an important number?"
coded_message = code(message) # => "Eo 42 wj eilknpwjp jqixan?"
decoded_message = decode(coded_message) # => "Is 42 an important number?"

Related

Method to reverse string only if it has less than four letters

I need to write a ruby method that reverses a string only if it has less than four characters.
# Write a method that reverses
# a string ONLY if it's shorter than
# 4 letters.
# Otherwise, the string is
# returned as-is.
# (Hint: strings have
# a built-in .length method!)
# conditional_reverse("yo")
# => "oy"
# conditional_reverse("hello")
# => "hello"
Here is the code I came up with.
def conditional_reverse(string)
good = string.length
if good < 4
puts string.reverse
else
puts string
end
puts conditional_reverse("cat")
end
When I run it in repl I get the following response
:conditional_reverse
I have no idea what i'm doing wrong.
just put puts conditional_reverse("cat") out side our def
def conditional_reverse(string)
good = string.length
if good < 4
puts string.reverse
else
puts string
end
end
conditional_reverse("cat")
You are callind your method in its definition. Avoid it if you are not writing a recursive method.
def conditional_reverse(s)
s.length < 4 ? s.reverse : s
end
The answer provided by #Ursus is perfect, but in case you want to go with your way the change you have to do is this;
def conditional_reverse(string)
good = string.length
if good < 4
puts string.reverse
else
puts string
end
end
puts conditional_reverse("cat")
What the others said, plus...
You get that response from irb because in recent versions of Ruby, a method definition returns the method name as a symbol.
Also, your problem specifies that the string should be reversed, not that the string should be output reversed; so you should remove the puts calls and just manipulate the string.
For the benefit of your readers, I recommend being specific with your names. good = string.length could be changed to needs_reversing = string.length < 4, for example.

Truncate string when it is too long

I have two strings:
short_string = "hello world"
long_string = "this is a very long long long .... string" # suppose more than 10000 chars
I want to change the default behavior of print to:
puts short_string
# => "hello world"
puts long_string
# => "this is a very long long....."
The long_string is only partially printed. I tried to change String#to_s, but it didn't work. Does anyone know how to do it like this?
updated
Actually i wanna it works smoothly, that means the following cases also work fine:
> puts very_long_str
> puts [very_long_str]
> puts {:a => very_long_str}
So i think the behavior belongs to String.
Thanks all anyway.
First of all, you need a method to truncate a string, either something like:
def truncate(string, max)
string.length > max ? "#{string[0...max]}..." : string
end
Or by extending String: (it's not recommended to alter core classes, though)
class String
def truncate(max)
length > max ? "#{self[0...max]}..." : self
end
end
Now you can call truncate when printing the string:
puts "short string".truncate
#=> short string
puts "a very, very, very, very long string".truncate
#=> a very, very, very, ...
Or you could just define your own puts:
def puts(string)
super(string.truncate(20))
end
puts "short string"
#=> short string
puts "a very, very, very, very long string"
#=> a very, very, very, ...
Note that Kernel#puts takes a variable number of arguments, you might want to change your puts method accordingly.
This is how Ruby on Rails does it in their String#truncate method as a monkey-patch:
class String
def truncate(truncate_at, options = {})
return dup unless length > truncate_at
options[:omission] ||= '...'
length_with_room_for_omission = truncate_at - options[:omission].length
stop = if options[:separator]
rindex(options[:separator], length_with_room_for_omission) ||
length_with_room_for_omission
else
length_with_room_for_omission
end
"#{self[0...stop]}#{options[:omission]}"
end
end
Then you can use it like this
'And they found that many people were sleeping better.'.truncate(25, omission: '... (continued)')
# => "And they f... (continued)"
You can write a wrapper around puts that handles truncation for you:
def pleasant(string, length = 32)
raise 'Pleasant: Length should be greater than 3' unless length > 3
truncated_string = string.to_s
if truncated_string.length > length
truncated_string = truncated_string[0...(length - 3)]
truncated_string += '...'
end
puts truncated_string
truncated_string
end
Truncate naturally
I want to propose a solution that truncates naturally. I fell in love with the String#truncate method offered by Ruby on Rails. It was already mentioned by #Oto Brglez above. Unfortunately I couldn't rewrite it for pure ruby. So I wrote this function.
def truncate(content, max)
if content.length > max
truncated = ""
collector = ""
content = content.split(" ")
content.each do |word|
word = word + " "
collector << word
truncated << word if collector.length < max
end
truncated = truncated.strip.chomp(",").concat("...")
else
truncated = content
end
return truncated
end
Example
Test: I am a sample phrase to show the result of this function.
NOT: I am a sample phrase to show the result of th...
BUT: I am a sample phrase to show the result of...
Note: I'm open for improvements because I'm convinced that there is a shorter solution possible.
You can just use this syntax:
"mystring"[0..MAX_LENGTH]
[5] pry(main)> "hello world"[0..10]
=> "hello world"
[6] pry(main)> "hello world why"[0..10]
=> "hello world"
[7] pry(main)> "hello"[0..10]
=> "hello"
There's no need to check if it actually exceed the maximum length.

Ruby modify a piece of a string

Totally new to Ruby. This is a simple homework assignment. The secret_code function needs to take in input string and perform the following actions:
In the first block of letters before a space, capitalize all but the first char
Reverse the string
So if the input were "super duper", the output should be "repud REPUs".
I coded the function as follows:
def secret_code(input)
input.split(" ").first[1..-1].each_char do |i|
input[i] = i.upcase
end
return input.reverse
end
It passes the unit tests, but I am wondering if there is a better way to code it. Is it possible to avoid using the loop? I tried
return input.split(" ").first[1..-1].upcase.reverse
But that didn't quite work. Any thoughts on how to clean this up are appreciated!
"super duper".sub(/(?<=.)\S+/, &:upcase).reverse
How about this:
def secret_code(input)
first_space = input.index(' ')
(input[0] + input[1...first_space].upcase + input[first_space..-1]).reverse
end
Note that in Ruby, the last expression evaluate in a method is always returned, so you can omit the final return.
s = "super duper"
words = s.split(' ')
words.first[1..-1] = words.first[1..-1].upcase
words.each { |word| word.reverse! }
s = words.reverse.join(' ')
puts s # => repud REPUs
Not necessarily any better, but sure, it can be done without a loop...
def f x
(b = [(a = x.split)[0].upcase, *a.drop(1)].join(' ').reverse)[-1] = x[0, 1]
return b
end
You can try the below:
a = "super duper"
p a.gsub(a.split[0...1].join(' '),a.split[0...1].join(' ').capitalize.swapcase).reverse
Output:
"repud REPUs"

How to generate unique six digit alpha-numeric code in Ruby

I need to generate a unique six digit alpha-numeric code. To save in my database as voucher no: for every transaction.
I used this
require 'sha1'
srand
seed = "--#{rand(10000)}--#{Time.now}--"
Digest::SHA1.hexdigest(seed)[0,6]
How to generate a random string in Ruby This link was useful
A better way is to let the database handle the ids(incrementing). But if you insisting on generating them your self, you can use a random generator to generate an code, check it against db for uniqueness. then either accept or regenerate
I'd use the database to generate unique keys, but if you insist on doing it the hard way:
class AlnumKey
def initialize
#chars = ('0' .. '9').to_a + ('a' .. 'z').to_a
end
def to_int(key)
i = 0
key.each_char do |ch|
i = i * #chars.length + #chars.index(ch)
end
i
end
def to_key(i)
s = ""
while i > 0
s += #chars[i % #chars.length]
i /= #chars.length
end
s.reverse
end
def next_key(last_key)
to_key(to_int(last_key) + 1)
end
end
al = AlnumKey.new
puts al.next_key("ab")
puts al.next_key("1")
puts al.next_key("zz")
Of course, you'll have to store your current key somewhere, and this is in no way thread / multisession-safe etc.
With the following restrictions:
Valid only until 2038-12-24 00:40:35 UTC
Generates no more than once within a second
you can use this simple code:
Time.now.to_i.to_s(36)
# => "lks3bn"
class IDSequence
attr_reader :current
def initialize(start=0,digits=6,base=36)
#id, #chars, #base = start, digits, base
end
def next
s = (#id+=1).to_s(#base)
#current = "0"*(#chars-s.length) << s
end
end
id = IDSequence.new
1234.times{ id.next }
puts id.current
#=> 0000ya
puts id.next
#=> 0000yb
9876543.times{ id.next }
puts id.current
#=> 05vpqq
This would eleviate the time collision issue by getting the milli seconds
(Time.now.to_f*1000.0).to_i

puts matches from to files

I have mac addresses in mac1.txt and mac2.txt and I wanted to do something like this:
v = File.open("/RubyDev/sort/mac1.txt",'r').each_line do |a|
w = File.open("/RubyDev/sort/mac2.txt",'r').each_line do |b|
if w in v
puts w
end
end
end
Thanks in advance for your assistance!
EDIT: That first version below is actually pretty terrible. Here's a better version:
lines = []
File.open("mac1.txt",'r').each_line do |a|
lines.push(a.rstrip!)
end
File.open("mac2.txt",'r').each_line do |b|
if lines.include?(b.rstrip!)
puts b
end
end
I think what you're looking for is something like this:
File.open("mac1.txt",'r').each_line do |a|
File.open("mac2.txt",'r').each_line do |b|
if a == b
puts b
end
end
end
Is that correct? If not, could you give more background about the issue and what you're trying to accomplish?
To get all the common lines between two files, you can use File#readlines to access all the lines in the file as an array. Keep in mind that they will still have newlines ("\n") appended, so you'll need to remove them with String#chomp. The easiest way to do this is to map the array that readlines gives you, like this:
common_macs = File.open("mac1.txt").readlines.map(&:chomp) &
File.open("mac2.txt").readlines.map(&:chomp)
I am still trying to learn Ruby, so this is unlikely a good solution, but it is a possibility. It reads the contents of the first file into a hash and then checks the contents of the second against it. I think it would be reasonably efficient (well ... unless the first file is too big to fit nicely in memory).
lines = Hash.new
File.open( "mac1.txt", 'r' ).each_line do |l|
lines[l] = true
end
File.open( "mac2.txt", 'r' ).each_line do |l|
if ( lines[l] == true )
puts l
end
end
Edit For completeness, here is the very succinct version as suggested in the comments by Mark Thomas with the white space removal suggested by Gavin Anderegg. Ruby is a sweet language.
lines = Hash.new
File.open( "mac1.txt", 'r' ).each_line {|l| lines[l.strip!] = true}
File.open( "mac2.txt", 'r' ).each_line {|l| puts l if lines[l.strip!]}
lines = {}
File.open("mac1.txt").each_line {|l| lines[l.chomp] = true}
File.open("mac2.txt").each_line {|l| puts l if lines[l.chomp]}

Resources