How to eliminate letter? - ruby

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

Related

private method called noMethodError ruby

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

How to extract each individual combination from a flat_map?

I'm fairly new to ruby and it's my first question here on stackoverflow so pardon me if I'm being a complete noob.
The code which i am working with contains this line -
puts (6..6).flat_map{|n| ('a'..'z').to_a.combination(n).map(&:join)}
What the code does is that its starts printing each of the combinations starting from "abcdef" and continues till the end (which i have never seen as it has 26^6 combinations).
Of course having an array of that size (26^6) is unimaginable hence I was wondering if there is any way by which i can get next combination in a variable, work with it, and then continue on to the next combination ?
For example I calculate the first combination as "abcdef" and store it in a variable 'combo' and use that variable somewhere and then the next combination is calculated and "abcdeg" is stored in 'combo' and hence the loop continues ?
Thanks
(6..6).flat_map { |n| ... } doesn't do much. Your code is equivalent to:
puts ('a'..'z').to_a.combination(6).map(&:join)
To process the values one by one, you can pass a block to combination:
('a'..'z').to_a.combination(6) do |combo|
puts combo.join
end
If no block is given, combination returns an Enumerator that can be iterated by calling next:
enum = ('a'..'z').to_a.combination(6)
#=> #<Enumerator: ["a", "b", "c", ..., "w", "x", "y", "z"]:combination(6)>
enum.next
#=> ["a", "b", "c", "d", "e", "f"]
enum.next
#=> ["a", "b", "c", "d", "e", "g"]
enum.next
#=> ["a", "b", "c", "d", "e", "h"]
Note that ('a'..'z').to_a.combination(6) will "only" yield 230,230 combinations:
('a'..'z').to_a.combination(6).size
#=> 230230
As opposed to 26 ^ 6 = 308,915,776. You are probably looking for repeated_permutation:
('a'..'z').to_a.repeated_permutation(6).size
#=> 308915776
Another way to iterate from "aaaaaa" to "zzzzzz" is a simple range:
('aaaaaa'..'zzzzzz').each do |combo|
puts combo
end
Or manually by calling String#succ: (this is what Range#each does under the hood)
'aaaaaa'.succ #=> "aaaaab"
'aaaaab'.succ #=> "aaaaac"
'aaaaaz'.succ #=> "aaaaba"

Ruby search for word in string

Given input = "helloworld"
The output should be output = ["hello", "world"]
Given I have a method called is_in_dict? which returns true if there's a word given
So far i tried:
ar = []
input.split("").each do |f|
ar << f if is_in_dict? f
// here need to check given char
end
How to achieve it in Ruby?
Instead of splitting the input into characters, you have to inspect all combinations, i.e. "h", "he", "hel", ... "helloworld", "e", "el" , "ell", ... "elloworld" and so on.
Something like this should work:
(0..input.size).to_a.combination(2).each do |a, b|
word = input[a...b]
ar << word if is_in_dict?(word)
end
#=> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
ar
#=> ["hello", "world"]
Or, using each_with_object, which returns the array:
(0..input.size).to_a.combination(2).each_with_object([]) do |(a, b), array|
word = input[a...b]
array << word if is_in_dict?(word)
end
#=> ["hello", "world"]
Another approach is to build a custom Enumerator:
class String
def each_combination
return to_enum(:each_combination) unless block_given?
(0..size).to_a.combination(2).each do |a, b|
yield self[a...b]
end
end
end
String#each_combination yields all combinations (instead of just the indices):
input.each_combination.to_a
#=> ["h", "he", "hel", "hell", "hello", "hellow", "hellowo", "hellowor", "helloworl", "helloworld", "e", "el", "ell", "ello", "ellow", "ellowo", "ellowor", "elloworl", "elloworld", "l", "ll", "llo", "llow", "llowo", "llowor", "lloworl", "lloworld", "l", "lo", "low", "lowo", "lowor", "loworl", "loworld", "o", "ow", "owo", "owor", "oworl", "oworld", "w", "wo", "wor", "worl", "world", "o", "or", "orl", "orld", "r", "rl", "rld", "l", "ld", "d"]
It can be used with select to easily filter specific words:
input.each_combination.select { |word| is_in_dict?(word) }
#=> ["hello", "world"]
This seems to be a task for recursion. In short you want to take letters one by one until you get a word which is in dictionary. This however will not guarantee that the result is correct, as the remaining letters may not form a words ('hell' + 'oworld'?). This is what I would do:
def split_words(string)
return [[]] if string == ''
chars = string.chars
word = ''
(1..string.length).map do
word += chars.shift
next unless is_in_dict?(word)
other_splits = split_words(chars.join)
next if other_splits.empty?
other_splits.map {|split| [word] + split }
end.compact.inject([], :+)
end
split_words('helloworld') #=> [['hello', 'world']] No hell!
It will also give you all possible splits, so pages with urls like penisland can be avoided
split_words('penisland') #=> [['pen', 'island'], [<the_other_solution>]]

why .downto method is not working?

Please see code as below:
"S".upto("Z") {|word| puts word}
It shows correct result.
But when I used .downto method, there was no result.
"Z".downto("S") {|word| puts word}
WHY?
it doesn't work because String doesn't have an downto method. It only has upto.
There is no downto method on Strings in Ruby (even though there is such a method for numbers). (Check: http://ruby-doc.org/core-2.0/String.html)
To that you may ask, "Why not?" And to that I have no answer.
there no .downto method for a String object but you can do:
('S'..'Z').to_a.reverse.each {|word| puts word}
OR
"S".upto("Z").to_a.reverse.each {|word| puts word}
to achieve the result you expected.
ell, there's a good reason for this: there's no method with that name declared for the String Class! Remember to look for the documentation of classes/methods when you're not sure.
But there's a workaround, don't worry, just extend the class with your own method. My try would be:
class String
def downto value
v = value.upto self
v.to_a.reverse.each
end
end
puts "S".upto("Z").collect {|x| x}
puts "Z".downto("S").collect {|x| x}
Would render
Upto : ["S", "T", "U", "V", "W", "X", "Y", "Z"]
Downto: ["Z", "Y", "X", "W", "V", "U", "T", "S"]

Eliminate consecutive duplicates of list elements

What is the best solution to eliminate consecutive duplicates of list elements?
list = compress(['a','a','a','a','b','c','c','a','a','d','e','e','e','e']).
p list # => # ['a','b','c','a','d','e']
I have this one:
def compress(list)
list.map.with_index do |element, index|
element unless element.equal? list[index+1]
end.compact
end
Ruby 1.9.2
Nice opportunity to use Enumerable#chunk, as long as your list doesn't contain nil:
list.chunk(&:itself).map(&:first)
For Ruby older than 2.2.x, you can require "backports/2.2.0/kernel/itself" or use {|x| x} instead of (&:itself).
For Ruby older than 1.9.2, you can require "backports/1.9.2/enumerable/chunk" to get a pure Ruby version of it.
Do this (provided that each element is a single character)
list.join.squeeze.split('')
Ruby 1.9+
list.select.with_index{|e,i| e != list[i+1]}
with respect to #sawa, who told me about with_index :)
As #Marc-André Lafortune noticed if there is nil at the end of your list it won't work for you. We can fix it with this ugly structure
list.select.with_index{|e,i| i < (list.size-1) and e != list[i+1]}
# Requires Ruby 1.8.7+ due to Object#tap
def compress(items)
last = nil
[].tap do |result|
items.each{ |o| result << o unless last==o; last=o }
end
end
list = compress(%w[ a a a a b c c a a d e e e e ])
p list
#=> ["a", "b", "c", "a", "d", "e"]
arr = ['a','a','a','a','b','c','c','a','a','d','e','e','e','e']
enum = arr.each
#=> #<Enumerator: ["a", "a", "a", "a", "b", "c", "c", "a", "a", "d",
# "e", "e", "e", "e"]:each>
a = []
loop do
n = enum.next
a << n unless n == enum.peek
end
a #=> ["a", "b", "c", "a", "d"]
Enumerator#peek raises a StopIteration exception when it has already returned the last element of the enumerator. Kernel#loop handles that exception by breaking out of the loop.
See Array#each and Enumerator#next. Kernel#to_enum1 can be used in place of Array#each.
1 to_enum is an Object instance method that is defined in the Kernel module but documented in the Object class. Got that?

Resources