Using Ruby hash key as parameters - ruby

I am trying to use a parameter as my key to find the value in a hash, and I just confused about why I couldn't get the value by the first way. I am new to Ruby.
def getCards(player,hash)
a =$player
puts "a = "+a.to_s
puts "a.class = "+a.class.to_s
puts " hash[:a]"+" #{hash[:a]}"
puts " hash[:'1']"+" #{hash[:"1"]}"
end
edit:
def getCards(player,hash)
puts player
#result successfully 1 or any number that I gets from console
puts hash[player]
# nothing but 1 is actually a key in my hash
# {1=>["yellow3", "yellow8", "green9", "black11", "red1", "black7", "red5", #"yellow7", more results ..
end

Note that Ruby is not PHP or Perl, so that should be player and not $player. Argument names and their corresponding use as variables are identical.
$player refers to the global variable of that name, which is unrelated and will be presumed to be undefined unless otherwise set.
Now if by hash[:a] you mean to access the contents of the hash under the key with the player value you've assigned to a then what you actually want is:
hash[player]
Where that represents looking up an entry with that key. a is a variable in this case, :a is the symbol "a" which is just a constant, like a label, which has no relation to the variable.
Don't forget that "#{x}" is equivalent to x.to_s so just use interpolation instead of this awkward "..." + x.to_s concatenation.
Another thing to keep in mind is that in Ruby case has significant meaning. Variable and method names should follow the get_cards style. Classes are ClassName and constants are like CONSTANT_NAME.

Related

Modifying arguments in Ruby

I apologize for the excessive length, I just wanted to make sure I actually understand what's going on here. This is a follow up on my previous two questions Does 'upcase!' not mutate a variable in Ruby? and Destructive and non destructive methods in Ruby.
def changeMe(string)
string + "?"
end
phrase = "how are you"
puts changeMe(phrase) #how are you?
puts phrase #how are you
When changeMe is invoked with the phrase passed in as an argument, the parameter string points to the same object as phrase. When we change the line to string + "?" we are creating a new object different from the one the string parameter points to, the same if we assigned the newly created object to a variable.
def changeMe(string)
string += "?"
end
phrase = "how are you"
puts changeMe(phrase) #how are you?
puts phrase #how are you
If I do this -
def changeMe(string)
string + "?"
string.capitalize!
end
phrase = "how are you"
puts changeMe(phrase) #How are you
puts phrase #How are you
When changeMe is invoked with phrase passed in as an argument, the string + "?" creates a new object different from the one #capitalize! is called on in the next line. #capitalize! is called on the object that the variable phrase is referencing, the same object the string parameter points to but not the same object returned by string + ?. If we reassign it to a variable,
def changeMe(string)
string += "?"
string.capitalize!
end
phrase = "how are you"
puts changeMe(phrase) #How are you?
puts phrase #how are you
string += "?" will create a new object that is assigned to a variable called string. That new object has #capitalize! called on it. The method is invoked with phrase passed in as an argument and returns a new object different from the one the variable phrase references so the original value for the variable phrase is unchanged.
Are there flaws or misconceptions in my logic. Am I accurately explaining/understanding things?
That's largely correct, but perhaps a more complicated path to understanding than necessary. In Ruby one thing that helps a lot is calling object_id on a given object to see which object it is. Every object has a unique object_id.
For example:
"test" == ("te" + "st")
# => true
"test".object_id == ("te" + "st").object_id
# => false
Or more specifically for a method that creates a new copy:
x = 'test'
y = x + '?'
x.object_id == y.object_id
# => false
You can see how in-place modifications work:
x = 'test'
y = x << '?'
x.object_id == y.object_id
# => true
Where this allows you to differentiate between in-place modifications and methods that produce new objects or copies.
Remember that every Ruby expression returns an object. If this object is not captured into a variable or used as an argument will often can be discarded if not already used.
In other words there's a huge difference between this:
def add
1 + 2 # Computed and discarded
:three # The actual return value
end
And this:
def add
1 + 2 # Computed and returned
end
Though this depends on that return value being captured, as calling the function computes the value and throws out the results again unless it's captured or used.

Ruby - Parameters by reference or by value? [duplicate]

This question already has answers here:
Is Ruby pass by reference or by value?
(14 answers)
Closed 8 years ago.
I don't understand why they say Ruby passes all parameters by value and at the same time the following code proves the opposite:
class MyClass1
#var1 = 123
def get1
#var1
end
def set1=value
#var1 = value
end
end
c1 = MyClass1.new
c1.set1 = 444
p c1.get1 # 444
def test1 mc
mc.set1 = 999
end
test1 c1
p c1.get1 # 999
If it were by value, it would print out 444, not 999.
This question confuses people because there is a thing called a reference type and there is a thing called pass-by-reference, but they don't actually have all that much to do with each other.
References and values and values that are references: A (sorta) brief overview
In a pass-by-reference scenario, the parameters of a function are references to the variables that were passed into the function, and modifying the parameters modifies the original variables. This is not what Ruby is. For example, let's look at the following code:
def inc(val)
val += 1
end
a = 1
inc a
puts a
If Ruby were a pass-by-reference language, this program would print 2, because the val += 1 in inc would increment the value of a. But that isn't what happens. The variable val is not a reference to the variable a — it's an independent variable that is given the same value.
"But wait!" you say. "What if we were dealing with objects? Surely object variables are passed by reference, right?"
Nope.
def change_string(str)
str << " I can insult you all you want"
str << " because you'll never see this"
str << " because I'm going to replace the whole string!"
str << " Haha you smell bad!"
str = "What? I didn't say anything." # I'm so sneaky
end
be_nice_to_me = "hello"
change_string(be_nice_to_me)
puts be_nice_to_me
If Ruby were pass-by-reference, you'd never see how mean the change_string method is, because the str = "What, I didn't say anything." would totally replace the value of be_nice_to_me with the string "What? I didn't say anything." But in fact change_string's sins are laid bare for all to see. How is this possible if Ruby doesn't pass by reference?
Well, remember those reference types I talked about earlier? Well, that's what objects are in Ruby. A reference type is a type whose value is a reference to something else. In this case, the variable's value is a reference to the string "hello". When you pass the string, the variable's value — which is a reference — is copied into the variable str. So now they both hold references to the same object, but str is not a reference to be_nice_to_me. So when you modify the object, those changes show up because they're both referring to the same object. But when you modify one variable, the other doesn't see it because neither variable is a reference to the other.
So is Ruby pass-by-reference or pass-by-value? It's pass-by-value, but all the values are references.

How to create a similar Ruby method using symbol?

I have been reading a Ruby book where I encountered below code to describe symbols
def walk(direction)
if direction == :north
# ...
end
end
I tried and failed to create a similar method ( where a comparison is made against a symbol such as
direction == :north
because most of the time I have seen symbols being used something like param[:name], so in my code I tried :north = 1 or :favourite = 'ruby' but got syntax error.
Is it really possible to have such a comparison using a symbol alone (without hash) ie instead of
if "ruby" == param[:name]
end
if "ruby" == :name
end
I am not sure if I have expressed the question clearly, if not I shall try and reword it.
I see a misunderstanding of what symbols are and what is their purpose.
if direction == :north
In this line, direction is a variable (it can hold any value) and :north is a value (it can be assigned to variables).
Trying to do this:
:north = 1
is like trying to do
2 = 1
Which doesn't make sense, of course.
Symbols are rather like identifiers, or a special version of strings.
With strings, you can have
str1 = 'SYM'
and
str2 = 'symbol'
str2 = str2[0,3].upcase
and now there are two identical strings in different places in memory. Ruby has to compare all the characters to evaluate str1 == str2.
However symbols are unique - you can't do character manipulation on them, and if you have
sym1 = :SYM
sym2 = :SYM
then it takes only a single comparison to test their equality. It demonstrates this clearly if we look at the object IDs for the strings and the symbols
puts str2.object_id
puts str1.object_id
puts sym1.object_id
puts sym2.object_id
puts str1.to_sym.object_id
puts str2.to_sym.object_id
output
22098264
22098228
203780
203780
203780
203780
So the two strings have different object IDs, whereas the two symbols are, in fact, the same object. Even converting the two strings to symbols gives the same object ID, so there is only one :SYM.
Because symbols are values, it makes no sense to write something like :north = 1 as it is like writing 'north' = 1.
Comparing strings to symbols, like 'north' = :north will always return false, because they are different classes of object.
param[:name] works only because you can index a hash with any object. (You can say param[Object.new] = 1.) It's different from writing either param['name'] (indexing the hash by a literal string) or param[name] (indexing the hash by the contents of variable name).
Does this answer your question?

How to iterate only a specific value position in a ruby hash?

I know the first value of all ##logHash keys contains IP addresses. I want to iterate just that position to create keys for a new hash if its not a duplicate key.
Here is what I have but I know it can't be right...
def ipaddresses(##logHash)
##ipHash = Hash.new
##logHash[1].each_value do | value |
if ##ipHash.has_key?(value)
##ipHash[value] += "#"
else
##ipHash[value] = "#"
end
puts ""
##ipHash.sort.each { |key,value| puts "The frequency of #{key} is |#{value}"}
end
end
Any help is appreciated, thanks!
Lisa
Here's a reworked version that might be closer to what you want:
def ipaddresses(logHash)
ipHash = Hash.new(0)
logHash[1].each_value do | value |
ipHash[value] += 1
puts ""
end
ipHash.sort.each { |key,value| puts "The frequency of #{key} is |#{value}"}
end
It's not clear why you're using ## class variables in a method like this. They're very unusual to be using in any context. For temporary variables or method arguments, no prefix is required.
Here Hash.new(0) creates a new hash with a default value of 0. This avoids having to pre-initialize the keys before using them as in Ruby adding anything to nil is considered invalid.
You cannot have a class variable (or anything other than a local variable) as an argument. It does not make sense to do that. Arguments are something that are passed together with a method call. If you want to refer to a class variable within the method definition, you can just refer to that directly. Having it passed via argument is redundant, and is hence made impossible by design.

Ruby call constructor with random parameters count

I have some classes like
class Demo1 < Struct.new(:text, :text2)
end
class Demo2 < Struct.new(:text, :text2, :text3)
end
How can I call constructor of each class if I only have name and hash of parameters
I need to write method like this,
but this is wrong becasue after send(:new,args) Struct will contain :text which equal to args
def call_demo_object(demo_name, args={})
demo_name.to_s.constantize.send(:new,args)
end
The mian problem is calling constructor with random parameters from hash
variant one:
def call_demo_object(demo_name, args={})
z = [':new']
args.keys.each do |key|
z.push "args[:"+key.to_s+"]"
end
eval('demo_name.to_s.constantize.send(' + z.join(', ') +')' )
end
variant two:
def call_demo_object(demo_name, args={})
a = demo_name.to_s.constantize.send(:new)
args.each do |key, value|
a[key] = value if a.members.include?(key)
end
a
end
One possible variant:
def call_demo_object(demo_name, args={})
obj = demo_name.new
obj.members.each do |member|
obj[member] = args[member]
end
obj
end
It's pros:
args can be in any order
only availible structure members will be assigned
I see a couple of things wrong:
Not sure if your classes really look like that, but you'll need end at the end of them, otherwise you'll get syntax errors.
Also, constantize is not a method on strings in Ruby, it's something Rails defines. So you'll need to use
Kernel.const_get(demo_name.to_s)
to get the same functionality.
As pointed out in the comments I neglected to mention how to expand the parameters.
To do that you'll need to use what's called the "splat operator"
Kernel.const_get(demo_name.to_s).send(:new,*args) #notice the * in front of args
That will expand args out.
However, when args is a hash, say {:text=>"hello", :text2=>"hello2"}, it will expand it out to an array with 2 elements where each element is an array with they key in the first position and key in the second position.
Instead, if you pass an array in as args with the objects in order, you will get what you're looking for.
I think if you're going for what amounts to named parameters, you might have to try another route, but I don't know that for sure.
To go with optional or named parameters, you might look at how Rails does it: use a hash for the parameter, then pass in a hash with the keys. You can then keep a valid list of keys and check the passed-in hash and either reject them or raise an error.

Resources