If I have the following:
puts "---doesn't change---"
def change_string(val)
val = "abc"
end
str = "some"
change_string(str)
puts str
puts "---does change---"
def change_string_two(val)
puts "val is of class: #{val.class}"
val << " extra"
puts "val is of class: #{val.class}"
end
change_string_two(str)
puts str
puts "str is of type: #{str.class}"
puts "---does change---"
str = "xxx"
def change_string_three(val)
val.concat(" more things")
end
change_string_three(str)
puts str
It ouptuts:
---doesn't change---
some
---does change---
val is of class: String
val is of class: String
some extra
str is of type: String
---does change---
xxx more things
I understand that it's passing an object reference but I'm confused as to how in one scenario it doesn't change and in two scenarios, it does change. Is this peculiar to strings in Ruby?
This is not peculiar to Ruby. val.concat("x") and val << "x" modify the value val refers to. Whenever you have a function where the argument being passed is a reference, then methods called on the argument which mutate it will result in mutations on the originally referenced thing.
An assignment like val = "abc" on the other hand reassigns the local variable val to refer to something new, and leaves what it previously referred to untouched.
If you want to replace the string rather than add to it, use val.replace("abc"), e.g.
def replace(val)
val.replace("abc")
end
str = "123"
replace(str)
puts str
outputs "abc".
Related
I know writing like
a=23
p "the value of a is #{a}"
it will print: the value of a is 23.
but now I am actually receiving this string as a parameter like
def evaluate string
a=23
puts string
end
calling method pass that string as a parameter
evaluate "the value of a is #{a}"
Is there any way to evaluate this string inside the method? puts string has to interpolate the value a=23.
Edit:
I have to read and execute the program from Excel.
At the first line,
Excel entry is,
"id=something" setvalue a
So now corresponding program will read the value from locator id=something and set it into the instance variable #a.
and user's next excel entry would be
"the value of a is 23" compare "the value of a is #{a}"
Now the program will read "the value of a is 23" and this "the value of a is #{a}" for comparison, but before it compares, it has to replace the value a. That's all I want. I hope now my question is very clear.
For ruby you can change how you "format" your strings in Excel, than you can use "classic" formatting
a = 23
s = 'the value of a is %s'
def evaluate(text, value)
puts text % value
end
You can use different formatting keys, for example %d for integers, %f for float numbers
You can use named arguments
dynamic_text = 'the value of the %<product_name>s is %<product_price>0.2f'
def evaluate(text, args)
puts text % args
end
name = "Product"
price = 78.99
evaluate dynamic_text, product_name: name, product_price: price
Without names, use order of the given values
dynamic_text = 'the value of the %s is %0.2f'
def evaluate(text, args)
puts text % args
end
name = "Product"
price = 78.99
evaluate dynamic_text, [name, price]
You can make a block and then evaluate the string:
def evaluate &block
a=23
block.call(a)
end
evaluate { |a| "the value of a is #{a}" } #=> "the value of a is 23"
It's a very odd thing you're attempting to do. When you have some sort of a pattern with placeholders, you do it like:
def evaluate(string)
a=23
format string, a: a
end
evaluate "the value of a is %{a}"
String interpolation with #{..} is not meant for the case you're describing as the value is evaluated at the time of constructing the string, not later. You could do some regexp matching and replace the #{..} with %{..} as a workaround.
There's a few ways:
"Code" Dynamic
lazy evaluation with lambdas:
def evaluate(str_template)
a = 23
str_template.call(a)
end
user_input = gets
my_lambda = lambda do |str|
user_input.size > 10 ? "dynamic 1 #{str}" : "dynamic 2 #{str}"
end
evaluate(my_lambda)
# => "dynamic 1/2 23"
This is "code dynamic", but not "input dynamic", i.e. you can't receive the string template from the user.
"Input" Dynamic 1
ERB templating:
require 'erb'
user_input_erb = gets
puts user_input_erb # "Hello <%= name %>"
name = gets # also user input, e.g. "World"
ERB.new(user_input_erb).result
# => "Hello World"
Note that in general, getting string templates from the user and evaluating them is a potential security vulnerability. If there's any possibility user input can be adversarial, you'll want to see if you can find a "guaranteed to be safe against all user input" string templating library.
"Input" Dynamic 2
user_input_template = gets
puts user_input_template # "Hello %s"
name = gets # also user input, e.g. "World"
user_input_template % name
# => "Hello World"
"Input" Dynamic 3
Really dangerous, but:
user_input_ruby_code = gets
puts user_input_ruby_code # '"Hello #{name}"'
name = gets # also user input, e.g. "World"
eval user_input_ruby_code # DANGER
# => "Hello World"
I get the following error when running a simple method that takes in a proper noun string and returns the string properly capitalized.
def format_name(str)
parts = str.split
arr = []
parts.map do |part|
if part[0].upcase
else part[1..-1].downcase
arr << part
end
end
return arr.join(" ")
end
Test cases:
puts format_name("chase WILSON") # => "Chase Wilson"
puts format_name("brian CrAwFoRd scoTT") # => "Brian Crawford Scott"
The only possibility that the above code returns a blank output is because your arr is nil or blank. And the reason your arr is blank(yes it is blank in your case) because of this line of code:
if part[0].upcase
in which the statement would always return true, because with every iteration it would check if the first element of the part string can be upcased or not, which is true.
Hence, your else block never gets executed, even if this got executed this would have returned the same string as the input because you are just putting the plain part into the array arr without any formatting done.
There are some ways you can get the above code working. I'll put two cases:
# one where your map method could work
def format_name(str)
parts = str.split
arr = []
arr = parts.map do |part|
part.capitalize
end
return arr.join(" ")
end
# one where your loop code logic works
def format_name(str)
parts = str.split
arr = []
parts.map do |part|
arr << "#{part[0].upcase}#{part[1..-1].downcase}"
end
return arr.join(" ")
end
There are numerous other ways this could work. I'll also put the one I prefer if I am using just plain ruby:
def format_name(str)
str.split(' ').map(&:capitalize)
end
You could also read more about the Open Classes concept to put this into the String class of ruby
Also, checkout camelize method if you're using rails.
I'm very new to programming so I'm sorry if I'm asking a really simple question. I've also already done my research and I still can't get what I want so I'm asking here.
So I'm writing a simple camelcase method - All words must have their first letter capitalized without spaces. Right now in order to call this function I have to type camelcase("hello there") which would return "Hello There" in interactive ruby. I am wondering how to convert this method into a different type of method(I think it's called a class method?) which would allow me to do this instead: "hello there".camelcase #=> "Hello There"
I've also seen that the syntax would be like so:
class String
def method()
...
end
end
But I really don't know how to apply it...
def camelcase(string)
newArray = []
newNewArray = []
array = string.split(" ")
for i in 0...array.length
newArray << array[i].capitalize
end
newNewArray = newArray.join(" ")
end
In this way. I've used your_camelcase because I'm not sure if that method does not exist in Ruby String class. Anyway, this is an instance method and you should use self to refer to your string
class String
def your_camelcase
newArray = []
newNewArray = []
array = self.split(" ")
for i in 0...array.length
newArray << array[i].capitalize
end
newArray.join(" ")
end
end
You're almost there. Just put that method into String class. Inside of that method, self will refer to the string. You don't need to (and can't) pass it as a parameter.
class String
def camelcase
newArray = []
newNewArray = []
array = self.split(" ")
for i in 0...array.length
newArray << array[i].capitalize
end
newNewArray = newArray.join(" ")
end
end
'hello there'.camelcase # => "Hello There"
The output you want is not called Camel Case. Camelcase examples are camelCase or CamelCase (no spaces).
If you just want to capitalize each word, it is called Title Case. A naive implementation of titlecase is like so:
class String
def titlecase
self.split.map(&:capitalize).join(" ")
end
end
"hello world".titlecase #=> "Hello World"
Note: to make it a true camelcase implementation, you would replace join(" ") with join.
As you said, each instance you want to change is a string, so we'll need to add the method you want to the String class:
class String
def camelcasify
newArray = []
newNewArray = []
array = string.split(" ")
for i in 0...array.length
newArray << array[i].capitalize
end
newNewArray = newArray.join(" ")
end
end
However... rails comes with a method for this built in, so you shouldn't need to do the above. Try:
"string".camelize
I ran into a study drill problem, and I couldn't figure it out.
Here's the link to the exercise. https://learnrubythehardway.org/book/ex40.html
Below are my work. On Study Drill 2, I passed in variables and it worked.
However, at study drill 3, I broke my code. I realized I wasn't passing in variable, but a hash. And because my class takes in 2 arguments, I couldn't figure out how to pass a dictionary as 2 arguments.
class Song
def initialize(lyrics, singer)
#lyrics = lyrics
#singer = singer
end
def sing_along()
#lyrics.each {|line| puts line}
end
def singer_name()
puts "The song is composed by #{#singer}"
end
def line_reader(lineNum)
line = #lyrics[lineNum-1]
puts "The lyrics line #{lineNum} is \"#{line}\"."
end
end
# The lyrics are arrays, so they have [] brackets
practiceSing = Song.new(["This is line 1",
"This is line 2",
"This is line 3"],"PracticeBand")
practiceSing.sing_along()
practiceSing.singer_name()
practiceSing.line_reader(3)
puts "." * 20
puts "\n"
# Variable for passing. Working on dictionary to pass the singer value.
lovingThis = {["Don't know if I'm right",
"but let's see if this works",
"I hope it does"] => 'TestingBand'}
# Everything after this line is somewhat bugged
# Because I was using a variable as an argument
# I couldn't figure out how to use dictionary or function to work with
this
practiceVariable = Song.new(lovingThis,lovingThis)
practiceVariable.sing_along()
practiceVariable.singer_name()
practiceVariable.line_reader(3)
Here's the Output. What it should do is return the singer/band, and return requested lyrics line.
I'm new to coding, please advise how to pass hashes into classes?
How to pass lovingThis hash into Song.new() and read as 2 arguments?
you can pass hash to constructor of class in the same way as we pass any other variable, But for that you need to change your constructor definition to take variable number of arguments i.e def initialize(*args)
class Song
def initialize(*args)
if args[0].instance_of? Hash
#lyrics = args[0].keys.first
#singer = args[0].values.first
else
#lyrics = args[0]
#singer = args[1]
end
end
def sing_along()
#lyrics.each {|line| puts line}
end
def singer_name()
puts "The song is composed by #{#singer}"
end
def line_reader(lineNum)
line = #lyrics[lineNum-1]
puts "The lyrics line #{lineNum} is \"#{line}\"."
end
end
# The lyrics are arrays, so they have [] brackets
practiceSing = Song.new(["This is line 1",
"This is line 2",
"This is line 3"],"PracticeBand")
practiceSing.sing_along()
practiceSing.singer_name()
practiceSing.line_reader(3)
puts "." * 20
puts "\n"
# Variable for passing. Working on dictionary to pass the singer value.
lovingThis = {["Don't know if I'm right",
"but let's see if this works",
"I hope it does"] => 'TestingBand'}
practiceVariable = Song.new(lovingThis)
practiceVariable.sing_along()
practiceVariable.singer_name()
practiceVariable.line_reader(3)
i read string methods in ruby. i understood replace will replace the string with the argument passed to it. But the same thing we can do with a short and sweet = oparator also. what is the point of using replace method? its just a personnel choice or it's different from an = operator?
> a = "hello world"
=> "hello world"
> a = "123"
=> "123"
> a.replace("345")
=> "345"
Using =
str = "cat in the hat"
str.object_id
#=> 70331872197480
def a(str)
str = "hat on the cat"
puts "in method str=#{str}, str.object_id=#{str.object_id}"
end
a(str)
in method str=hat on the cat, str.object_id=70331873031040
str
#=> "cat in the hat"
str.object_id
#=> 70331872197480
The values of str outside the method and str inside the method are different objects.
Using String#replace
str = "cat in the hat"
str.object_id
#=> 70331872931060
def b(str)
str.replace("hat on the cat")
puts "in method str=#{str}, str.object_id=#{str.object_id}"
end
b(str)
in method str=hat on the cat, str.object_id=70331872931060
str
#=> "hat on the cat"
str.object_id
#=> 70331872931060
The values of str outside the method and str inside the method are the same object.
This line of code changes the variable a to point to a new string:
a = "new string"
This line of code actually changes the string object that a (and possibly other variables) are pointing to:
a.replace "new string"
The use case is really just, to achieve something much like pass-by-reference in other languages, where a variable's value can be changed directly. So you could pass a String to a method and that method may entirely change the string to something else.
def bar(bazzer)
bazzer.replace("reference")
end
bar(baz)
=> It's reference because local assignment is above the food chain , but it's clearly pass-by-reference
This makes sense.