String assignment does not work properly - ruby

I am trying to write a function that takes a string and takes desired indices and scrambles the string:
def scramble_string(string, positions)
temp = string
for i in 0..(string.length-1)
temp[i] = string[positions[i]]
end
puts(string)
return temp
end
When I call the above method, the "string" is altered, which you will see in the output of puts.
Why does this happen since I didn't put string on the left-hand side of an equation I wouldn't expect it to be altered.

You need a string.dup:
def scramble_string(string, positions)
temp = string.dup
for i in 0..(string.length-1)
temp[i] = string[positions[i]]
end
puts(string)
return temp
end
For more understanding try the following snippet:
string = 'a'
temp = string
puts string.object_id
puts temp.object_id
The result of two identical object ids, in other words, is that both variables are the same object.
With:
string = 'a'
temp = string.dup
puts string.object_id
puts temp.object_id
puts string.object_id == temp.object_id #Test for same equal -> false
puts string.equal?( temp) #Test for same equal -> false
puts string == temp #test for same content -> true
you get two different objects, but with the same content.

Related

ruby - access array with hash key

I am struggling to understand how I can access an array with a hash key. In my code, I create a hash with keys and values. Now, I want to set the values in a Car class. Whenever I try to instantiate the Car, the argument expects Integer and not a String.
I am getting the following error: TypeError (no implicit conversion of String into Integer)
Here is my code:
class Car_maker
attr_accessor :car_maker
def initialize(car_maker)
#car_maker = car_maker
end
end
class Car_model < Car_maker
attr_accessor :km, :type, :transmission, :stock, :drivetrain, :status,
:fuel, :car_maker, :model, :year, :trim, :features
#total number of instances & array with car objects
##totalCars = 0
##catalogue = []
def initialize(km, type, transmission, stock, drivetrain, status, fuel, car_maker, model, year, trim, features)
super(car_maker)
#km = km
#type = type
#transmission = transmission
#stock = stock
#drivetrain = drivetrain
#status = status
#fuel = fuel
#model = model
#year = year
#trim = trim
#features = features
##totalCars += 1
end
def self.convertListings2Catalogue(line)
#Initialise arrays and use them to compare
type = ["Sedan", "coupe", "hatchback", "station", "SUV"]
transmission = ["auto", "manual", "steptronic"]
drivetrain = ["FWD", "RWD", "AWD"]
status = ["new", "used"]
car_maker = ["honda", "toyota", "mercedes", "bmw", "lexus"]
hash = Hash.new
#In this part, we hash the set of features using regex
copyOfLine = line
regex = Regexp.new(/{(.*?)}/)
match_array = copyOfLine.scan(regex)
match_array.each do |line|
hash["features"] = line
end
#Now, we split every comma and start matching fields
newStr = line[0...line.index('{')] + line[line.index('}')+1...line.length]
arrayOfElements = newStr.split(',')
arrayOfElements.each do |value|
if value.include?("km") and !value.include?("/")
hash["km"] = value
elsif type.include?(value)
hash["type"] = value
elsif transmission.include?(value.downcase)
hash["transmission"] = value
elsif value.include?("/") and value.include?("km")
hash["fuel economy"] = value
elsif drivetrain.include?(value)
hash["drivetrain"] = value
elsif status.include?(value.downcase)
hash["status"] = value
elsif /(?=.*[a-zA-Z])(?=.*[0-9])/.match(value) and !value.include?("km")
hash["stock"] = value
elsif car_maker.include?(value.downcase)
hash["carmaker"] = value
elsif /^\d{4}$/.match(value)
hash["year"] = value
elsif value.length == 2
hash["trim"] = value
else
if value.length > 2
hash["model"] = value
end
end
end
end
end
textFile = File.open('cars.txt', 'r')
textFile.each_line{|line|
if line.length > 2
result = Car_model.convertListings2Catalogue(line)
puts "Hash: #{result}"
carObj = Car_model.new(result["km"], result["type"], result["transmission"], result["stock"], result["drivetrain"],
result["status"], result["fuel"], result["carmaker"], result["model"], result["year"], result["trim"], result["features"])
###catalogue.push (carObj)
end
}
This line
result = Car_model.convertListings2Catalogue(line)
Doesn't return the hash object. It returns arrayOfElements since that's what the each method actually returns and the each method is the last method executed in the method (although there are hash assignments within it, it's only the last value that's returned unless you use an explicit return statement.
Just use the variable hash in the last line of the convertListing2Catalog method
if value.length > 2
hash["model"] = value
end
end
end
hash # < this is the last line of the method so it's the value that will be returned
end
end
If you think about it, there were several variables created in the method. There's no reason to expect that the contents of any specific variable such as hash would be returned, and ruby methods by default return the last executed command.

Chop last character of string

I want to make program which takes the string and chop last character each time and print result to console:
With an input string of Hello, the result should be:
Hello
Hell
Hel
He
H
This is my code so far:
def test_string
puts "Put your string in: "
string = gets.chomp
while string.length == 0
puts string.chop(/.$/)
end
end
puts test_string
Use chop!:
string = gets.chomp
# Print full string, e.g. "Hello"
puts string
# Print remaining... e.g. "Hell", "Hel", etc.
while string.length != 0
puts string.chop!
end
Following code does not modify the original string
string = gets.chomp
l = string.length
l.times do |i|
puts string[0..(l-i-1)]
end
You can also create an array filling it with the string N times, and for each time, get a character less from it:
str = 'Hello'
Array.new(str.size) { |index| str[0...str.size - index] }.each { |str| p str }
# "Hello"
# "Hell"
# "Hel"
# "He"
# "H

ruby trie implementation reference issue

I am trying to implement a trie in Ruby but can't figure out what the problem is with my print + collect methods.
I just implemented the same in JS and working fine. I guess the issue could be that Ruby is passed by reference (unlike JS) and how variable assignment works in Ruby.
So if I run the code with string.clone as argument when I recursively call the collect function then I get:
["peter", "peter", "petera", "pdanny", "pdjane", "pdjanck"]
and if I pass string then:
["peterradannyjaneck", "peterradannyjaneck", "peterradannyjaneck", "peterradannyjaneck", "peterradannyjaneck", "peterradannyjaneck"]
Any ideas how to fix this?
the code:
class Node
attr_accessor :hash, :end_node, :data
def initialize
#hash = {}
#end_node = false
#data = data
end
def end_node?
end_node
end
end
class Trie
def initialize
#root = Node.new
#words = []
end
def add(input, data, node = #root)
if input.empty?
node.data = data
node.end_node = true
elsif node.hash.keys.include?(input[0])
add(input[1..-1], data, node.hash[input[0]])
else
node.hash[input[0]] = Node.new
add(input[1..-1], data, node.hash[input[0]])
end
end
def print(node = #root)
collect(node, '')
#words
end
private
def collect(node, string)
if node.hash.size > 0
for letter in node.hash.keys
string = string.concat(letter)
collect(node.hash[letter], string.clone)
end
#words << string if node.end_node?
else
string.length > 0 ? #words << string : nil
end
end
end
trie = Trie.new
trie.add('peter', date: '1988-02-26')
trie.add('petra', date: '1977-02-12')
trie.add('danny', date: '1998-04-21')
trie.add('jane', date: '1985-05-08')
trie.add('jack', date: '1994-11-04')
trie.add('pete', date: '1977-12-18')
print trie.print
Ruby's string concat mutates the string and doesn't return a new string. You may want the + operator instead. So basically change the 2 lines inside collect's for-loop as per below:
stringn = string + letter
collect(node.hash[letter], stringn)
Also, you probably want to either always initialize #words to empty in print before calling collect, or make it a local variable in print and pass it to collect.

I am creating a caesar cipher in Ruby but the caesar function never finishes

Here is my code:
def caesar(string, shift_factor)
alphabet = Array("a".."z")
new_alph = alphabet.rotate(shift_factor)
new_str = []
new_str = string.downcase.split("")
new_str.each do |i|
print i
if !alphabet.include?(i)
new_str.push(i)
else
equals = alphabet.index(i)
new_str.push(new_alph[equals])
end
end
end
caesar("What a string!", 0)
print new_str.join.capitalize!
The code just keeps on looping and I am not sure how to go about stopping it.
You need a different variable for storing the result string. How about this:
def caesar(string, shift_factor)
alphabet = Array("a".."z")
new_alph = alphabet.rotate(shift_factor)
new_str = string.downcase.split("")
caesar_string = []
new_str.each do |i|
if !alphabet.include?(i)
caesar_string.push(i)
else
equals = alphabet.index(i)
caesar_string.push(new_alph[equals])
end
end
caesar_string
end
caesar_string = caesar("What a string!", 0)
print caesar_string.join.capitalize!
You're iterating over new_str and in each iteration you're pushing another object onto the array so the loop will never end.
In your loop, if you instead replace the character at the index, then you should get the result you're looking for.
def caesar(string, shift_factor)
alphabet = Array("a".."z")
new_alph = alphabet.rotate(shift_factor)
new_str = string.downcase.split("")
new_str.each_with_index do |letter, i|
if !alphabet.include?(letter)
new_str[i] = letter
else
equals = alphabet.index(letter)
new_str[i] = new_alph[equals]
end
end
end
Just to add an example that in Ruby there is always more than one way of doing things:
def caesar(string, shift_factor)
alphabet = ('a'..'z').to_a
string.downcase.tr(alphabet.join, alphabet.rotate(shift_factor).join)
end

Ruby method to reverse string input

I don't get why reversed_string=string[i] + reversed_string puts the last char first. It seems that string[i] would index the first char and not the last. So if the string was "abc" index 0 would be 'a' and not 'c'. Could someone please explain how ruby gets 'c' from index 0? And then, of course, 'b' from index 1? Etc, etc.
Write a method that will take a string as input, and return a new string with the same letters in reverse order.
Difficulty: easy.
def reverse(string)
reversed_string = ""
i = 0
while i < string.length
reversed_string = string[i] + reversed_string
i += 1
end
return reversed_string
end
puts("reverse(\"abc\") == \"cba\": #{reverse("abc") == "cba"}")
puts("reverse(\"a\") == \"a\": #{reverse("a") == "a"}")
puts("reverse(\"\") == \"\": #{reverse("") == ""}")
reversed_string = string[i] + reversed_string
For example, if string is "abc", string[0] is indeed "a", but here it's being put in the beginning of reversed_string, not the end. reversed_string is added up in each iteration as:
"a" + "" #string[0] + "" => "a"
"b" + "a" #string[1] + "a" => "ba"
"c" + "ba" #string[2] + "ba"=> "cba"
Assuming you can't use Ruby Class String's built in Reverse method, you could try the following
def reverse_string(string)
new_string = []
i = string.length-1
while i >= 0
new_string.push(string[i])
i -= 1
end
new_string.join
end
This will create a new string object, but it will reverse the string without using any built-in methods.
As you know, there is a method String#reverse to reverse a string. I understand you are not to use that method, but instead write your own, where the method's argument is the string to be reversed. Others will suggest ways you might do that.
As you are new to Ruby, I thought it might be instructive to show you how you could write a new method for the String class, say, String#my_reverse, that behaves exactly the same as String#reverse. Then for the string "three blind mice", we would have:
"three blind mice".reverse #=> "ecim dnilb eerht"
"three blind mice".my_reverse #=> "ecim dnilb eerht"
To create a method without arguments for the String class, we normally do it like this:
class String
def my_method
...
end
end
We invoke my_method by sending it a receiver that is an instance of the String class. For example, if write:
"three blind mice".my_method
we are sending the method String#my_method to the receiver "three blind mice". Within the definition of the method, the receiver is referred to as self. Here self would be "three blind mice". Similarly, just as the second character (at offset 1) of that string is "three blind mice"[1] #=> "h", self[1] #=> "h". We can check that:
class String
def my_method
puts "I is '#{self}'"
(0...self.size).each { |i| puts self[i] }
end
end
"three blind mice".my_method
would print:
I is 'three blind mice'
t
h
r
e
e
b
...
c
e
The method my_reverse is almost the same:
class String
def my_reverse
sz = self.size
str = ''
(0...sz).each { |i| str << self[sz-1-i] }
str
end
end
"three blind mice".my_reverse
#=> "ecim dnilb eerht"
You can think of self as a variable whose value is the receiver, but unlike a variable, you cannot reassign self to a different object. For example, we can write x = 1; x = 'cat', but we cannot write self = 'cat'. As we have already seen, however, we can change the references self makes to other objects, such as self[1] = 'r'.

Resources