private method called noMethodError ruby - 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

Related

How to eliminate letter?

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

How do I access the current array when using map/select

I have the following (working) code I'm trying to convert into a more concise snippet using either #map or #select.
def duplicate_string(string)
s_list = []
string.downcase.chars.each do |char|
if string.index(char) != string.rindex(char) && s_list.include?(char) == false
s_list << char if char != ' '
end
end
s_list
end
puts duplicate_string("I am a duplicate string") == ["i", "a", "t"] #true
This is what I've come up with so far, but I don't know how to access the current array that's been stored by #map or #select and using self isn't working
def duplicate_string_with_map(string)
string.downcase.chars.select { |char| string.index(char) != string.rindex(char) && self.include?(char) == false && char != ' ' }
end
The following code would solve your purpose:
def duplicate_string_with_map(string)
(string.downcase.chars.select { |char| string.index(char) != string.rindex(char) && char != ' ' }).uniq
end
Here you need not check include condition as you are already ensuring string.index(char) != string.rindex(char).
However, for a better ruby approach, I would suggest you to re-open String class and write a method there.
It would look something like this:
class String
def duplicate_characters_array
(downcase.chars.select { |char| index(char) != rindex(char) && char != ' ' }).uniq
end
end
string = "I am a duplicate string"
string.duplicate_characters_array
You don't need to access the array and you don't need to use Array#map.
There are many ways to reach the goal. One of them is to split the string in chars then group the chars (get a hash), reject the groups of space character and the groups smaller than two elements and return the keys of the remaining groups:
"I am a duplicate string"
.downcase
.chars
.group_by{|i| i}
.reject{|k, v| k == ' ' || v.length < 2}
.keys
# ["a", "i", "t"]
Here we can make use of a helper method, Array#difference. The method is explained here. Note that that link contains a link to an SO answer where I cite examples of its use. Though I proposed that the method be added to the Ruby core there appears to be little interest in doing so.
class Array
def difference(other)
h = other.each_with_object(Hash.new(0)) { |e,h| h[e] += 1 }
reject { |e| h[e] > 0 && h[e] -= 1 }
end
end
Here we can use this helper as follows.
def duplicate_string(str)
a = str.gsub(/\s/,'').downcase.reverse.chars
a.difference(a.uniq).uniq.reverse
end
duplicate_string "I am a duplicate string"
#=> ["a", "i", "t"]
The steps are as follows.
str = "I am a duplicate string"
b = str.gsub(/\s/,'')
#=> "Iamaduplicatestring"
c = b.downcase
#=> "iamaduplicatestring"
d = c.reverse
#=> "gnirtsetacilpudamai"
a = d.chars
#=> ["g", "n", "i", "r", "t", "s", "e", "t", "a", "c", "i", "l", "p",
# "u", "d", "a", "m", "a", "i"]
e = a.uniq
#=> ["g", "n", "i", "r", "t", "s", "e", "a", "c", "l", "p", "u", "d", "m"]
f = a.difference(e)
#=> ["t", "i", "a", "a", "i"]
g = f.uniq
#=> ["t", "i", "a"]
g.reverse
#=> ["a", "i", "t"]
The key step is the calculation of f. For each element c of e, f contains n-1 instances of c, where n is the number of instances of c in a. The method therefore excludes characters other than spaces that appear in the string exactly once.

Why the Ruby each iterator goes first in the execution?

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"

Enumerator `Array#each` 's {block} can't always change array values?

Ok maybe this is simple but...
given this:
arr = ("a".."z").to_a
arr
=> ["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"]
..and that I'm trying to change all "arr" values to "bad"
why isn't this working ?
arr.each { |v| v = "bad" }
arr
=> ["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"]
Answers suggested that "v" is a local variable to the block (a "copy" of the array value) and I fully understand that (and never puzzled me before) but then
.. why it is working if array elements are objects ?
class Person
def initialize
#age = 0
end
attr_accessor :age
end
kid = Person.new
man = Person.new
arr = [kid, man]
arr.each { |p| p.age = 50 }
arr[0]
=> #<Person:0xf98298 #age=50>
isn't here "p" still local to the block here?
but then it really affects the objects, how come ?
I'll expand upon #pst's comment:
why isn't this working ?
arr.each { |v| v = "bad" }
Because each iterates through the array and puts each item into the block you've given as a local variable v, as v is not a reference to the array arr.
new_arr = arr.each { |v| v = "bad" }
each does not give back an array, for that you would use map (see #benjaminbenben's answer). Therefore assigning it does not "work".
arr.each { |v| arr[arr.index v] = "bad" }
Here you put each item in arr into the local variable v, but you've also referred to the array itself in the block, hence you are able to assign to the array and use the local variable v to find an index that corresponds to the contents of v (but you may find this wouldn't work as you expect when the items are not all unique).
arr.each { |p| p.age = 50 }
kid.age #-> 50
Here, again you've filled the local variable p with each item/object in arr, but then you've accessed each item via a method, so you are able to change that item - you are not changing the array. It's different because the reference is to the contents of the local variable, which you've mixed up with being a reference to the array. They are separate things.
In response to the comment below:
arr[0]
# => #<Person:0xf98298 #age=50>
It's all about who's referring to whom when.
Try this:
v = Person.new
# => #<Person:0x000001008de248 #age=0>
w = Person.new
# => #<Person:0x000001008d8050 #age=0>
x = v
# => #<Person:0x000001008de248 #age=0>
v = Person.new
# => #<Person:0x00000100877e80 #age=0>
arr = [v,w,x]
# => [#<Person:0x00000100877e80 #age=0>, #<Person:0x000001008d8050 #age=0>, #<Person:0x000001008de248 #age=0>]
v referred to 2 different objects there. v is not a fixed thing, it's a name. At first it refers to #<Person:0x000001008de248 #age=0>, then it refers to #<Person:0x00000100877e80 #age=0>.
Now try this:
arr.each { |v| v = "bad" }
# => [#<Person:0x00000100877e80 #age=0>, #<Person:0x000001008d8050 #age=0>, #<Person:0x000001008de248 #age=0>]
They are all objects but nothing was updated or "worked". Why? Because when the block is first entered, v refers to the item in the array that was yielded (given). So on first iteration v is #<Person:0x00000100877e80 #age=0>.
But, we then assign "bad" to v. We are not assigning "bad" to the first index of the array because we aren't referencing the array at all. arr is the reference to the array. Put arr inside the block and you can alter it:
arr.each { |v|
arr[0] = "bad" # yes, a bad idea!
}
Why then does arr.each { |p| p.age = 50 } update the items in the array? Because p refers to the objects that also happen to be in the array. On first iteration p refers to the object also known as kid, and kid has an age= method and you stick 50 in it. kid is also the first item in the array, but you're talking about kid not the array. You could do this:
arr.each { |p| p = "bad"; p.age }
NoMethodError: undefined method `age' for "bad":String
At first, p referred to the object that also happened to be in the array (that's where it was yielded from), but then p was made to refer to "bad".
each iterates over the array and yields a value on each iteration. You only get the value not the array. If you want to update an array you either do:
new_arr = arr.map{|v| v = "bad" }
new_arr = arr.map{|v| "bad" } # same thing
or
arr.map!{|v| v = "bad"}
arr.map!{|v| "bad"} # same thing
as map returns an array filled with the return value of the block. map! will update the reference you called it on with an array filled with the return value of the block. Generally, it's a bad idea to update an object when iterating over it anyway. I find it's always better to think of it as creating a new array, and then you can use the ! methods as a shortcut.
In example
arr.each { |v| v = "bad" }
"v" is just reference to string, when you do v = "bad", you reassign local variable. To make everything bad you can do like that:
arr.each { |v| v.replace "bad" }
Next time you can play with Object#object_id
puts arr[0].object_id #will be save as object_id in first iteration bellow
arr.each { |v| puts v.object_id }
You might be looking for .map - which returns a new array with the the return value of the block for each element.
arr.map { "bad" }
=> ["bad", "bad", "bad", "bad", …]
using .map! will alter the contents of the original array rather than return a new one.
How about this
arry = Array.new(arry.length,"bad")
This will set the a default value of "bad" to the arry.length

Determining if a prefix exists in a set

Given a set of strings, say:
"Alice"
"Bob"
"C"
"Ca"
"Car"
"Carol"
"Caroling"
"Carousel"
and given a single string, say:
"Carolers"
I would like a function that returns the smallest prefix not already inside the array.
For the above example, the function should return: "Caro". (A subsequent call would return "Carole")
I am very new to Ruby, and although I could probably hack out something ugly (using my C/C++/Objective-C brain), I would like to learn how to properly (elegantly?) code this up.
There's a little known magical module in Ruby called Abbrev.
require 'abbrev'
abbreviations = Abbrev::abbrev([
"Alice",
"Bob",
"C",
"Ca",
"Car",
"Carol",
"Caroling",
"Carousel"
])
carolers = Abbrev::abbrev(%w[Carolers])
(carolers.keys - abbreviations.keys).sort.first # => "Caro"
Above I took the first element but this shows what else would be available.
pp (carolers.keys - abbreviations.keys).sort
# >> ["Caro", "Carole", "Caroler", "Carolers"]
Wrap all the above in a function, compute the resulting missing elements, and then iterate over them yielding them to a block, or use an enumerator to return them one-by-one.
This is what is generated for a single word. For an array it is more complex.
require 'pp'
pp Abbrev::abbrev(['cat'])
# >> {"ca"=>"cat", "c"=>"cat", "cat"=>"cat"}
pp Abbrev::abbrev(['cat', 'car', 'cattle', 'carrier'])
# >> {"cattl"=>"cattle",
# >> "catt"=>"cattle",
# >> "cat"=>"cat",
# >> "carrie"=>"carrier",
# >> "carri"=>"carrier",
# >> "carr"=>"carrier",
# >> "car"=>"car",
# >> "cattle"=>"cattle",
# >> "carrier"=>"carrier"}
Your question still doesn't match what you are expecting as a result. It seems that you need prefixes, not the substrings (as "a" would be the shortest substring not already in the array). For searching the prefix, this should suffice:
array = [
"Alice",
"Bob",
"C",
"Ca",
"Car",
"Carol",
"Caroling",
"Carousel",
]
str = 'Carolers'
(0..str.length).map{|i|
str[0..i]
}.find{|s| !array.member?(s)}
I am not a Ruby expert, but I think you may want to approach this problem by converting your set into a trie. Once you have the trie constructed, your problem can be solved simply by walking down from the root of the trie, following all of the edges for the letters in the word, until you either find a node that is not marked as a word or walk off the trie. In either case, you've found a node that isn't part of any word, and you have the shortest prefix of your word in question that doesn't already exist inside of the set. Moreover, this would let you run any number of prefix checks quickly, since after you've built up the trie the algorithm takes time at most linear in the length of the string.
Hope this helps!
I'm not really sure what you're asking for other than an example of some Ruby code to find common prefixes. I'll assume you want to find the smallest string which is a prefix of the most number of strings in the given set. Here's an example implementation:
class PrefixFinder
def initialize(words)
#words = Hash[*words.map{|x|[x,x]}.flatten]
end
def next_prefix
max=0; biggest=nil
#words.keys.sort.each do |word|
0.upto(word.size-1) do |len|
substr=word[0..len]; regex=Regexp.new("^" + substr)
next if #words[substr]
count = #words.keys.find_all {|x| x=~regex}.size
max, biggest = [count, substr] if count > max
#puts "OK: s=#{substr}, biggest=#{biggest.inspect}"
end
end
#words[biggest] = biggest if biggest
biggest
end
end
pf = PrefixFinder.new(%w(C Ca Car Carol Caroled Carolers))
pf.next_prefix # => "Caro"
pf.next_prefix # => "Carole"
pf.next_prefix # => "Caroler"
pf.next_prefix # => nil
No comment on the performance (or correctness) of this code but it does show some Ruby idioms (instance variables, iteration, hashing, etc).
=> inn = ["Alice","Bob","C","Ca","Car","Carol","Caroling","Carousel"]
=> y = Array.new
=> str="Carolers"
Split the given string to an array
=> x=str.split('')
# ["C","a","r","o","l","e","r","s"]
Form all the combination
=> x.each_index {|i| y << x.take(i+1)}
# [["c"], ["c", "a"], ["c", "a", "r"], ["c", "a", "r", "o"], ["c", "a", "r", "o", "l"], ["c", "a", "r", "o", "l", "e"], ["c", "a", "r", "o", "l", "e", "r"], ["c", "a", "r", "o", "l", "e", "r", "s"]]
Using Join to concatenate the
=> y = y.map {|s| s.join }
# ["c", "ca", "car", "caro", "carol", "carole", "caroler", "carolers"]
Select the first item from the y thats not available in the input Array
=> y.select {|item| !inn.include? item}.first
You will get "caro"
Putting together all
def FindFirstMissingItem(srcArray,strtocheck)
y=Array.new
x=strtocheck.split('')
x.each_index {|i| y << x.take(i+1)}
y=y.map {|s| s.join}
y.select {|item| !srcArray.include? item}.first
end
And call
=> inn = ["Alice","Bob","C","Ca","Car","Carol","Caroling","Carousel"]
=> str="Carolers"
FindFirstMissingItem inn,str
Very simple version (but not very Rubyish):
str = 'Carolers'
ar = %w(Alice Bob C Ca Car Carol Caroling Carousel)
substr = str[0, n=1]
substr = str[0, n+=1] while ar.include? substr
puts substr

Resources