I want a string which eliminates every "x, X". For example Xylophone -> ylophone, Xbox -> bo.
This is my recent code:
class String
def xaway
s = self.split("")
return ( s - ["xX"] ).join
end
end
What is wrong?
What is wrong?
Splitting the string by "" returns an array of characters, e.g.:
"Xbox".split("")
#=> ["X", "b", "o", "x"]
And Array#- expects an array of elements to remove. But if you pass ["Xx"] it tries to remove the element "Xx" which doesn't exist in the array:
["X", "b", "o", "x"] - ["Xx"]
#=> ["X", "b", "o", "x"]
What you want is ["X", "x"]:
["X", "b", "o", "x"] - ["X", "x"]
#=> ["b", "o"]
The whole code:
class String
def xaway
(split("") - ["X", "x"]).join
end
end
A little more concise:
class String
def xaway
(chars - %w[X x]).join
end
end
Or, using String#delete:
class String
def xaway
delete("Xx")
end
end
Note that you shouldn't modify Ruby's core classes.
You might be better off using the tr function, depending on your usage requirements:
class String
def xaway
tr("xX","")
end
end
I have a class like this:
class Example
DEFAULT_VALUE = {
'first_key': ['a', 'b'],
'second_key': 'c'
}
def append_new_value(value)
default_value_copy = DEFAULT_VALUE
default_value_copy[:first_key] << value
puts "default_value_copy: #{default_value_copy}"
puts "DEFAULT_VALUE: #{DEFAULT_VALUE}"
end
end
example = Example.new
example.append_new_value('d')
example.append_new_value('e')
The results are:
default_value_copy: {:first_key=>["a", "b", "d"], :second_key=>"c"}
DEFAULT_VALUE: {:first_key=>["a", "b", "d"], :second_key=>"c"}
default_value_copy: {:first_key=>["a", "b", "d", "e"], :second_key=>"c"}
DEFAULT_VALUE: {:first_key=>["a", "b", "d", "e"], :second_key=>"c"}
As I understood before, the value of DEFAULT_VALUE should not be changed after calling append_new_value method.
Could you guys help me to explain about this case?
First of all, Ruby does not have the notion of constants like you would expect from other languages. Ruby constants can change values. If you want to specify that an object should not be mutated in Ruby you must use the Object#freeze method on it.
Then, there is the matter of Ruby passing method arguments by reference or by value. You could say that Ruby is pass-by-value in the traditional sense. However in Ruby all variables are references to objects, so if you pass an object to a method it will indeed get mutated.
If you're looking for quick wins in immutability, check out Object#dup.
I've came across a weird thing doing simple tasks in Ruby. I just want to iterate the alphabet with the each method but the iteration goes first in the execution:
alfawit = ("a".."z")
puts "That's an alphabet: \n\n #{ alfawit.each { |litera| puts litera } } "
and this code results in this: (abbreviated)
a
b
c
⋮
x
y
z
That's an alphabet:
a..z
Any ideas why it works like this or what supposedly I did wrong?
Thanks in advance.
Because your each call is interpolated in your string literal that's executed before the fixed string. Also, each returns an Enumerable, in fact you print even that. Try this one
alfawit = ("a".."z")
puts "That's an alphabet: \n\n"
alfawit.each { |litera| puts litera }
or
puts "That's an alphabet: \n\n"
("a".."z").each { |litera| puts litera }
you can use interpolation if you want but in this way
alfawit = ("a".."z")
puts "That's an alphabet: \n\n#{alfawit.to_a.join("\n")}"
You can easily see what's going on if you extract the interpolation part into a variable:
alfawit = ("a".."z")
foo = alfawit.each { |litera| puts litera }
puts "That's an alphabet: \n\n #{ foo } "
The second line is causing the trouble: each invokes the block for each element of the range and then returns the receiver, so that foo becomes alfawit.
Here's another way to get the desired result:
alfawit = "a".."z"
puts "That's an alphabet:", alfawit.to_a
puts outputs each argument on a new line, but for array arguments, it outputs each element on a new line. Result:
That's an alphabet:
a
b
c
⋮
x
y
z
Likewise, you can turn the range into an argument list via *:
alfawit = "a".."z"
puts "That's an alphabet:", *alfawit
That's equivalent to:
puts "That's an alphabet:", "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"
I've been trying to work the following problem out and ran into the error. The point of the problem is to use a given key sequence to encrypt a string. For example, when given "cat" and [1,2,3] the result should be "dcw"
Any suggestions? the error was the following
def vigenere_cipher(string, key_sequence)
keyIndex=0
string=string.each_char.map do |c|
c=c.shift!(c,keyIndex)
keyIndex+=1
if keyIndex=key_sequence.length
keyIndex=0
end
end
return string
end
def shift!(c,keyIndex)
alphabet = ["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"]
inititalLetterIndex=alphabet.index(c)
finalLetterIndex=alphabet[inititalLetterIndex+keyIndex]
return alphabet[finalLetterIndex]
end
vigenere_cipher("cat", [1,2,3])
# private method `shift!' called for "c":String (NoMethodError)
You are trying to call shift! on string object that does not define on String Class, instead you defined on main object. You can call it like shift!(c,keyIndex) instead of c.shift!(c,keyIndex)
If you want to call you method shift! on a string, you will have to define it on String class.
class String
def shift!(keyIndex)
# you can access `c` using `self` here
...
end
end
Then you can call it as c.shift!(keyIndex) (Note the arguments are different).
Step 1
cipher.rb:4:in `block in vigenere_cipher': private method `shift!' called for "c":String (NoMethodError)
shift! isn't defined in String class, but at the top level.
So replace c=c.shift!(c,keyIndex) by c=shift!(c,keyIndex)
Step 2
cipher.rb:17:in `[]': no implicit conversion of String into Integer (TypeError)
Line 16 defines :
finalLetterIndex=alphabet[inititalLetterIndex+keyIndex]
alphabet contains letters as Strings, so finalLetterIndex isn't an index (Numeric), but a String.
On line 17, you try to use this String as an index.
Replace line 16 with :
finalLetterIndex=inititalLetterIndex+keyIndex
Step 3
Your script doesn't raise any exception anymore. It also doesn't display anything, so add a puts to the last line :
puts vigenere_cipher("cat", [1,2,3]).inspect
It returns :
[0, 0, 0]
Step 4
keyIndex seems to be stuck at 0. Why?
Look at line 6 :
if keyIndex=key_sequence.length
It doesn't test an equality, it assigns keyIndex to key_sequence.length.
Since any number is truthy in Ruby, it executes the code inside the if statement. Replace with
if keyIndex==key_sequence.length
Step 5
Your code returns [nil, nil, 0]. Why?
string is defined as the result of map. map returns an Array, in which each element is the result of the last executed command inside the block : in this case, the if statement.
if returns nil when the condition isn't satisfied, and returns the last executed command otherwise. In this case 0.
Add c at the last line of your map block.
Step 6
Your code now returns ["c", "b", "v"]. Why?
You only shift by shiftIndex, not by the amount defined in key_sequence Array. Replace
c=shift!(c,keyIndex)
with
c=shift!(c,key_sequence[keyIndex])
Step 7
Your code returns ["d", "c", "w"]. Almost there!
Ruby is a dynamic language. You're free to overwrite the String string with an Array, but it will confuse others and your future self.
Use array or letters instead of string, and return letters.join
Your script now returns "dcw".
It should look like :
def vigenere_cipher(string, key_sequence)
keyIndex=0
letters=string.each_char.map do |c|
c=shift!(c,key_sequence[keyIndex])
keyIndex+=1
if keyIndex==key_sequence.length
keyIndex=0
end
c
end
return letters.join
end
def shift!(c,keyIndex)
alphabet = ["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"]
inititalLetterIndex=alphabet.index(c)
finalLetterIndex=inititalLetterIndex+keyIndex
return alphabet[finalLetterIndex]
end
Step 8
vigenere_cipher("Hello", [1,2,3])
raises
cipher.rb:17:in 'shift!': undefined method '+' for nil:NilClass (NoMethodError).
Well, 'H' isn't found in your alphabet. Use downcase :
array=string.downcase.each_char.map do |c|
Step 9
vigenere_cipher("Hello World", [1,2,3])
doesn't work either, because of the space. Delete anything that isn't a letter :
array=string.downcase.delete('^a-z').each_char.map do |c|
Step 10
vigenere_cipher("zzz", [1,2,3])
returns an empty String, because there's no letter after z.
Use modulo 26 :
return alphabet[finalLetterIndex%26]
Step 11
Remove typos, don't use camelCase for variables, remove unnecessary return and you get :
def vigenere_cipher(string, key_sequence)
key_index = 0
letters = string.downcase.delete('^a-z').each_char.map do |c|
c = shift(c, key_sequence[key_index])
key_index = (key_index + 1) % key_sequence.length
c
end
letters.join
end
def shift(c, key_index)
alphabet = ('a'..'z').to_a
initial_letter_index = alphabet.index(c)
final_letter_index = initial_letter_index + key_index
alphabet[final_letter_index % 26]
end
Step 12
Using each_char, zip and cycle, I'd rewrite the whole code this way :
class Integer
# 0 => 'a', 1 => 'b', ..., 25 => 'z', 26 => 'a'
def to_letter
('a'.ord + self % 26).chr
end
end
class String
# 'A' => '0', 'a' => 0, ..., 'z' => 25
def to_code
self.downcase.ord - 'a'.ord
end
end
def vigenere_cipher(string, key)
short_string = string.delete('^A-Za-z')
short_string.each_char.zip(key.cycle).map do |char, shift|
(char.to_code + shift).to_letter
end.join
end
Step 13
Wikipedia article uses a String as key :
def vigenere_cipher(string, key)
short_string = string.delete('^A-Za-z')
short_string.each_char.zip(key.each_char.cycle).map do |char, shift|
(char.to_code + shift.to_code).to_letter
end.join
end
vigenere_cipher('Attack at dawn!', 'LEMON').upcase # => "LXFOPVEFRNHR"
Step 14
You should also be able to decrypt the message :
def vigenere_cipher(string, key, decrypt = false)
short_string = string.delete('^A-Za-z')
short_string.each_char.zip(key.each_char.cycle).map do |char, shift|
(char.to_code + shift.to_code * (decrypt ? -1 : 1)).to_letter
end.join
end
vigenere_cipher("LXFOPVEFRNHR", 'LEMON', :decrypt) #=> "attackatdawn"
Well, that was longer than expected! :D
I'm trying to break down a sentence that's part of one string, but break it down into letters in their own array and have that inside one big array.
So what I mean is:
def break("hello world")
the code in the method would than result in this:
[["h","e","l","l","o], ["w","o","r","l","d"]]
The reason why I need it like that is so I can rearrange the letters in the order I want later. I've tried several things, but no luck.
"hello world".split.map &:chars
# => [["h", "e", "l", "l", "o"], ["w", "o", "r", "l", "d"]]
I wouldn't use break as a method name. It's a key word in the language.
def break_it(str)
str.split.map { |word| word.each_char.to_a }
end
break_it("hello world")