Multiple method calling - ruby

This just returns the original name.
name = "George"
name.reverse.upcase!
puts(name)
I'm wondering why and if there is any way to do what I tried above.

reverse returns a new string. upcase! upcases the string it is called on in-place. You are creating a new reversed string, upcasing that new string, and then never using it again.
If you wanted to reverse and upcase the original string you could name.reverse!.upcase!

The methods you are calling do not affect the calling object. name.reverse will return a new String and leave the original alone. What you want to do is reassign name after your call.
name = George
name = name.reverse.upcase
There is a gotcha here in that bang methods, ending in ! will often modify the object being operated upon. So you could do something like below:
name = George
name.reverse!.upcase!
In general, I would avoid the ! methods unless you have a good reason. The first example of setting "name = " is very clear, easy to read and unambiguous.

Perhaps this will help explain what's happening:
name = "George" # => "George"
name.object_id # => 70228500576040
object_id is the memory reference for the actual variable, in other words, where it lives as the script is running.
reversed_name = name.reverse # => "egroeG"
reversed_name.object_id # => 70228500574980
We can tell that reverse created a new variable because the object_id is different from that of name.
upcased_reversed_name = reversed_name.upcase! # => "EGROEG"
upcased_reversed_name.object_id # => 70228500574980
The upcase! method modified the same variable as reversed_name.
If we use upcase instead, the object_id changes because a new version of the variable is created:
upcased_reversed_name = reversed_name.upcase # => "EGROEG"
upcased_reversed_name.object_id # => 70228500572960
upcased_reversed_name # => "EGROEG"
The short lesson is you can't assign a ! method result to a variable because it acts on the original variable and changes it in place.

Related

My Inner Hash is replacing the existing value when I assign a new value

I am storing the result in hash like this
I have assigned the result like this
Result['UserCreation']={"Test1"=>"Rajagopalan"}
So it created the hash like this
{"UserCreation"=>{"Test1"=>"Rajagopalan"}}
Now, I don't know how to assign another result for Test2. When I tend to assign result like this
Result['UserCreation']={"Test2"=>"Kali"}
It's replacing the existing result, and it's correctly doing it's Job, but I want to create result hash like given below when I assign the Result of Test2
{"UserCreation"=>{"Test1"=>"Rajagopalan","Test2"=>"Kali"}}
How can I achieve this?
Let us assume in this order I receive parameters
'UserCreation',{"Test1"=>"Rajagopalan"},
'UserCreation',{"Test2"=>"Kali"}
'contactcreate',{"Test2"=>"Kali"}
Result
{"UserCreation"=>{"Test1"=>"Rajagopalan","Test2"=>"Kali"},'contactcreate'=>{"Test2"=>"Kali"}}
All these values are the parameter to the functions.
You should use Hash#merge! method:
Result['UserCreation'].merge!({"Test2"=>"Kali"})
Here's a brief explanation:
When you use the assignment (Result['UserCreation']={"Test2"=>"Kali"}) you completely replace the value for the particular hash key. If you want to add (merge) something inside the existing hash you should use merge! method.
Notice that you can use Hash#merge! method because you know that the value of Result['UserCreation'] is a hash itself.
Also notice that there's merge method without bang (!). The difference that bang-version will mutate (change) your object. Consider this:
hash = {}
hash.merge({'one' => 1})
# hash variable will hold its initial value
# because `merge` method will not change it.
p hash # => {}
hash.merge!('one' => 1)
# This time we use bang-version, so hash variable
# will be updated.
p hash # => {"one"=>1}
One more thing about Ruby, notice how in the bang-version we omit curly braces. It's possible to do it if the last argument you passing to the method is a Hash.
Also, by convention in Ruby snake-case is using for variable and method naming, i.e.
result = {}
result['user_creation'] = {'test_1' => 'Rajagopalan'}
result['user_creation'].merge!('test_2' => 'Kali')
Of course, there's a field to play. For example, you can set the initial value like this:
result = {'user_creation' => {}}
result['user_creation'].merge!('test_1' => 'Rajagopalan')
result['user_creation'].merge!('test_2' => 'Kali')
or event update several pairs:
result = {'user_creation' => {}}
result['user_creation'].merge!(
'test_1' => 'Rajagopalan',
'test_2' => 'Kali'
)
UPDATE
For your case if you receive these parameters:
'UserCreation',{"Test1"=>"Rajagopalan"},
'UserCreation',{"Test2"=>"Kali"}
'contactcreate',{"Test2"=>"Kali"}
suppose that the first parameter named kind and the last one named value:
# kind = 'UserCreation' and value = '{"Test1"=>"Rajagopalan"}'.
result = {}
# Here we check `result[kind]` if there's no key, a new hash will
# be assigned, otherwise the existing value will be used.
result[kind] ||= {}
result[kind].merge!(value)
Maybe you want to use Hash#store:
result = {}
result['UserCreation'] = {"Test1"=>"Rajagopalan"}
result['UserCreation'].store("Test2", "Kali")
result #=> {"UserCreation"=>{"Test1"=>"Rajagopalan", "Test2"=>"Kali"}}

Does it matter which way a string method is used?

Codeacademy teaches that you can chain multiple methods together as such:
user_input.method1.method2.method3
However, in a later lesson they display some methods like this:
user_input = gets.chomp
user_input.downcase!
I combined them:
user_input = gets.chomp.downcase!
When I use it this way:
user_input = gets.chomp.downcase!
if user_input.include? "s"
...
I receive an error "undefined method `include?'". If I change it to the following, it works fine:
user_input = gets.chomp
user_input.downcase!
if user_input.include? "s"
...
I'm at a loss. I'm concerned whether or not this is a quirk with their console or if this is just how I should be doing it in Ruby. If someone could tell me which way is right, I'd appreciate it. If both are right, that's OK too.
Firstly, in case you do not yet fully understand, assignment of values to variables are done through =, and that you could inspect what variable type it is by appending .class to anything.
Consider the following:
name = 'John'
puts name
# => John
puts name.class
# => String
Now, secondly, it should be noted that the return values of ALL methods are ALL different. But all of them can be identified into two types:
Methods that:
return self
return anything other than self
Example for 1.
-- methods that return self, which you could say methods that return the same type of object which in our specific case, a String
name = 'John'
puts name
# => 'John'
puts name.class
# => String
downcased_name = name.downcase
puts downcased_name
# => john
puts downcased_name.class
# => String
deleted_downcased_name = downcased_name.delete('h')
puts deleted_downcased_name
# => jon
puts deleted_downcased_name.class
# => String
# All of the above can be just simplified into:
deleted_downcased_name2 = 'John'.downcase.delete('h')
puts deleted_downcased_name2
# => jon
puts deleted_downcased_name2.class
# => String
Notice that deleted_downcased_name and deleted_downcased_name2 are the same, because you could treat the chained methods as if you are chaining the return values which is 'John' -> 'john' -> 'jon'.
Example for 2
-- methods that return anything but self, which you could say methods that return a different type.
In our specific case, String's downcase! returns either a String or NilClass (reference here)
returning String if the string changes, or
returning nil if string is already downcased to begin with (no change).
or another String's method: start_with? (reference here)
returning true or false
This is where chaining of methods will not work (raises an error), when you try to use a String method as a chain to nil value.
Consider the following
name = 'mary'
puts name
# => 'mary'
puts name.class
# => String
downcased_name = name.downcase!
puts downcased_name
# => nil
puts downcased_name.class
# => NilClass
downcased_name.delete('h')
# => This will raise the following error
# NoMethodError: undefined method `delete' for nil:NilClass
The error above is because downcased_name is a type of NilClass where you are expecting it to be a type of String. Therefore you cannot chain any string method on it anymore. You can only chain String methods on a String type of value. Similarly, you can only chain Number methods on a Number type of value.
Whenever in doubt, you could always check the documentation to check what a method does, and what its return value and type.
The problem you are encountering is with the bang method downcase!.
This is basically saying "mutate the original string so that it is downcase".
The important part is that this returns nil. As such you are actually calling include? on nil.
If you use the non bang method downcase instead, it is saying "downcase the previously chained thing but do not mutate the original". The key difference is that it returns the result rather than nil.
Here is an example:
str = "ABCD"
str.downcase!
=> nil
str
=> "abcd"
str = "ABCD"
str.downcase
=> "abcd"
str
=> "ABCD" # Note str is still unchanged unless you set str = str.downcase
Welcome to Ruby! While your apprenticeship at Codeacademy may be limited, you'll continue to refer to language API documentation throughout your career. API documentation is a description of what the language (or a library) does for you. In this case, you're using downcase! which, as one commenter points out, does not always return a String. When it takes no action, it returns nil. Nil is an Object in Ruby (like everything else), but the 'include?' method isn't defined for nil, which explains your error. (It's one of the most common errors in Ruby, learn its meaning.)
So, in fact, what's breaking here isn't your method chain. It's that one of the intermediate methods isn't returning a value of the type you expect (nil instead of some kind of String).
Chaining non destructive methods like:
string.chomp.downcase...
has the advantage that the code is concise, but is not efficient if you are not interested in the original state of the object, and just want the final result because it creates intermediate objects during the chain.
On the other hand, applying destructive methods sequentially to the same object:
string.chomp!
string.downcase!
...
is more efficient if you do not need to keep the original state of the object, but is not concise.
Combining methods that may return an object of a different class (particularly nil) as:
string = gets.chomp!.downcase!...
is wrong because the result can become nil at some point in the chain.
Applying a potentially nil-returning method at only the last position as you did:
string = gets.chomp.downcase!
is still not useful if you expect string to always be a string, and can easily lead to an error as you did.
If you want to chain these methods in you example, perhaps you can do this:
user_input = gets.tap(&:chomp!).tap(&:downcase!)
if user_input.include?("s")
...

Exclamation mark and assignment inside of a function

Can someone explain why this:
def do_something str
str = "bar"
end
​
str_main = "foo"
do_something str_main
​
puts str_main
displays foo?
And this:
def do_something str
str.capitalize!
end
​
str_main = "foo"
do_something str_main
​
puts str_main
displays Foo?
Because of the way Ruby passes arguments.
When the method is being called, you have two references, str_main and str, to the same object "foo".
In the first example, when you use str = "bar", you are just changing what the str reference points to. So now you have str_main -> "foo" and str -> "bar". Therefore, the original object is not changed.
In the second example, you didn't change the str reference and changed the string in place with a mutator method, thus changing the same object that str_main points to.
The exclamation mark or bang operator modifies the original value. It is a destructive method. For example, let's say you had a string
string = "hi";
If you call the upcase method, you will get the following
string.upcase
=> "HI"
However, if you call string again, you will get the initial value.
string
=> "hi"
Now, let's say you use the destructive method upcase!
string.upcase!
=> "HI"
Now, if you call string again, you will see that the value was mutated.
string
=> "HI"
In Ruby, references are passed by value. So, a reference to str_main is passed to method do_something, a copy of reference is present in variable str.
This, however, does not mean that value that is referred to by both variables also has been copied around - there is still a single copy of referred to value, which is the string defined in Main.
Hence, when you assign a new value to str, this does not alter the value of str_main. However, when you modify the value that is referred by str, its changes are visibble outside.
All ruby methods return the last thing evaluated. However, object assignment stays within the scope of the current code block. Assigning str_main to a new value within a method will not affect str_main, unless it was an instance variable (#str_main). Doing such allows you to reassign an object across scopes, or depths, of your program. This is why your first method outputs 'foo' instead of 'bar'.
Now, the second example. #capitalize is a method called on a string object. It returns a new String instance, where its value is original object capitalized.
string = 'foobar'
string.capitalize # => 'Foobar'
puts string # => 'foobar'
Notice how string is only modified temporarily, and when called again it is back to normal.
Many methods in ruby have counterparts ending in !. This convention is the same as: object = object.some_method. Instead of creating a new instance of an object, these methods edit the original object's value. In the case of #capitalize!, the string is capitalized and modified for future calls.
string = 'foo'
string.capitalize! # => 'Foo'
puts string # => 'Foo'
Back to your second example. Using the #capitalize! method within the scope of do_something allows you to modify the str_main object. In a similar way to making str_main an instance variable.

ruby unintended variable assignment/change

i'm quite a layman in programming and a noob to ruby but find it useful for my work anyway. Cuirrently I work on a rather large script which brought the following unintended effect:
def my_reduce_method(value_hash,some_keys)
value_hash.delete(some_keys)
end
puts all_values
=> all_values
some_values = all_values # start my block with all values (class: hash)
some_values = my_reduce_method(some_values,keys_to_reduce)
# here only some_values should be effected!
puts all_values
=> some_values
Right in the block there is no damage, but the original all_values is lost! How can I ensure that in a certain code block a certain variable is definitely not changed?
Thank you in advance for any input!!!
All object assignments in Ruby are reference assignments.
That means, when you do:
some_values = all_values
You're copying the reference(or address) of the object which all_values is referencing(or pointing).
The solution for your case is simple:
some_values = all_values.clone
.dup also works usually (can be different depending on the object).
Another thing to be careful about is, when all_values[:x] has a string and you do:
some_values = all_values.clone
some_values[:x] += 'abc'
This will not change all_values[:x] because some_values[:x] gets (is assigned) a new string object.
But if you do:
some_values = all_values.clone
some_values[:x] << 'abc'
Both all_values[:x] and some_values[:x] change, because they both reference the same string object.
This is the effect of the shallow copy #Plasmarob mentioned.
Object#clone preserves the frozen attribute and singleton methods. If you
don't need those, what happens to your example, Object#dup is sufficient.

Changing value of ruby variables/references

I just stumbled upon something i don't quite understand. I know that variables in ruby are references. So that awesome stuff is possible. But when i pass a variable to a method, it behaves strangely:
my_var_a = "nothing happend to me"
my_var_b = "nothing happend to me"
def parse_set(my_var_set)
my_var_set = "my value changed"
end
def parse_sub(my_var_sub)
my_var_sub.sub! /(.*)/, "my value changed"
end
parse_set(my_var_a)
parse_sub(my_var_b)
my_var_a # => "nothing happend to me"
my_var_b # => "my value changed"
Can you explain to me why it works with sub! and = leaves the object unchanged? How can I avoid to use sub! but having the same result?
my_var_a and my_var_set are different references, but they point at the same object. If you modify the object in my_var_set, the change shows up in my_var_a. However, if you repoint my_var_set at a new object, that doesn't change what my_var_a points at.
Edit: clarification...
What Ruby does is called passing references by value. When you say
my_var_a = "nothing happend to me"
Ruby saves the string "nothing happend to me" in a memory location (let's call it 1000), and saves the my_var_a reference in another memory location (let's say 2000). When your code uses my_var_a, the interpreter looks at location 2000, see that it points to 1000, then gets the actual string value from 1000.
When you call parse_set(my_var_a), Ruby actually creates a new reference named my_var_set and points it to the string that my_var_a was pointing at (memory location 1000). However, my_var_set is a copy of the my_var_a reference -- let's say my_var_set was created at memory location 3000. my_var_a and my_var_set are 2 completely different references in memory, they just happen to point at the same exact memory location which holds the string value.
The statement my_var_set = "my value changed" in parse_set creates a new string in memory and points my_var_set at that new memory location. However, this doesn't change what the original my_var_a reference points at! Now that my_var_set points at a different memory location, nothing that you do to that variable will affect my_var_a.
The same reference copy happens for parse_sub as well. But the reason that parse_sub changes the string is because you're calling a method directly on the my_var_sub reference. When you do this, the interpreter gets the object that my_var_sub is pointing at and then modifies it. So that change will show up in the my_var_a reference, because it still points at the same string.
irb(main):008:0* a = 'abc'
=> "abc"
irb(main):009:0> a.replace('def')
=> "def"
irb(main):010:0> a
=> "def"
I've had to use replace exactly zero times in all the years I've been programming in Ruby. I wonder if there's a better design for your code.
Most string methods which change the receiver are suffixed by ! (sub!, capitalize!, chomp!, etc.) Some that change the receiver are not suffixed by ! (replace is one). If you call a method that changes the receiver, any and all references to that object will see the change. if you call a method that does not change receiver, the method returns a new string.
gsub does not modify the receiver, but instead returns a new instance of String:
a = "foo"
b = a
p a.sub(/o/, '#') # "f##"
p a # => "foo"
p b # => "foo"
gsub! does modify the receiver:
a = "foo"
b = a
p a.sub!(/o/, '#') # => "f#o"
p a # => "f#o"
p b # => "f#o"
"How can I avoid to use sub! but having the same result?"
def parse_set()
"my value changed"
end
a = parse_set()
Set it as an instance variable of an object (although this works just in your main script, I recommend making your own class if you want to use the instance variables approach).
#my_var_a = "nothing happend to me"
def parse_set()
#my_var_a = "my value changed"
end

Resources