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

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.

Related

How best to get all the format sequence keys from a string in ruby?

When given a string that is intended to be formatted with a hash of values to write into the string, is there a clean way to get all the keys that string is expecting values for?
I'm putting together text in a situation where there is a lot of room for customization, and several options for dynamic values to insert into the text. Some of the values are more expensive to get than others, so I'd like to be able to prepare my hash to send in to % to only include the values that are needed in the string.
Ideally I'd be able to query the system that performs the formatting on the string, but I'm not seeing any documentation of such an interface. What I'd like is something like:
"Your request for %{item} is at position %<pos>d".formatting_keys
>>> [:item, :pos]
When passing a hash to String#%, it will call the hash's default proc if a key is missing. You could utilize this behavior and make the proc sneakily collect the passed keys:
def format_keys(format_string)
keys = []
format_string % Hash.new { |_, k| keys << k ; 0 }
keys
end
format_keys("Your request for %{item} is at position %<pos>d")
#=> [:item, :pos]
Note that the proc's return value has to be a valid object for the various field types. I'm using 0 here which seems to work fine.
I'd like to be able to prepare my hash to send in to % to only include the values that are needed in the string.
Instead of a Hash, use an object that does the calculation on demand. That will be useful everywhere.
Use string interpolation to call the methods instead of format sequences.
class Whatever
def item
#item ||= calculate_item
end
def pos
#pos ||= calculate_pos
end
private
def calculate_item
# do something expensive
end
def calculate_pos
# do something expensive
end
end
obj = Whatever.new
puts "Your request for #{obj.item} is at position #{obj.pos.to_i}"
Using Ruby's own sequence parsing as per https://stackoverflow.com/a/74728162 is ideal, but you can also do your own:
class String
def format_keys
scan(
/
(?<!%) # don't match escaped sequence starts, e.g. "%%{"
(?:
(?<=%\{) [^\}]+ (?=\}) # contents of %{...}
| # OR
(?<=%\<) [^\>]+ (?=\>) # contents of %<...>
)
/x
)
end
end

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.

Using Ruby hash key as parameters

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.

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.

How does iteration work in Ruby?

I've recently started coding Ruby and I'm having a mis-understanding with block parameters.
Take the following code for example:
h = { # A hash that maps number names to digits
:one => 1, # The "arrows" show mappings: key=>value
:two => 2 # The colons indicate Symbol literals
}
h[:one] # => 1. Access a value by key
h[:three] = 3 # Add a new key/value pair to the hash
h.each do |key,value| # Iterate through the key/value pairs
print "#{value}:#{key}; " # Note variables substituted into string
end # Prints "1:one; 2:two; 3:three; "
I understand the general hash functionality, however I don't understand how value and key are set to anything. They are specified as parameters in the block, but the hash is never associated in any way with these parameters.
This is the Ruby block (Ruby's name for an anonymous function) syntax. And key, value are nothing but the arguments passed to the anonymous function.
Hash#each takes one parameter: A function which has 2 parameters, key and value.
So if we break it down into parts, this part of your code: h.each, is calling the each function on h. And this part of your code:
do |key, value| # Iterate through the key/value pairs
print "#{value}:#{key}; " # Note variables substituted into string
end # Prints "1:one; 2:two; 3:three;
is the function passed to each as an argument and key, value are arguments passed to this function. It doesn't matter what you name them, first argument expected is key and second argument expected is value.
Lets draw some analogies. Consider a basic function:
def name_of_function(arg1, arg1)
# Do stuff
end
# You'd call it such:
foo.name_of_function bar, baz # bar is becomes as arg1, baz becomes arg2
# As a block:
ooga = lambda { |arg1, arg2|
# Do stuff
}
# Note that this is exactly same as:
ooga = lambda do |arg1, arg2|
# Do stuff
end
# You would call it such:
ooga.call(bar, baz) # bar is becomes as arg1, baz becomes arg2
So your code can also be written as:
print_key_value = lambda{|arg1, arg2| print "#{arg1}:#{arg2}"}
h = {
:one => 1,
:two => 2
}
h.each &print_key_value
There are multiple ways in which the code inside a block can be executed:
yield
yield key, value # This is one possible way in which Hash#each can use a block
yield item
block.call
block.call(key, value) # This is another way in which Hash#each can use a block
block.call(item)
The hash (h) is associated with the loop due to you calling h.each rather than calling each on something else. It's effectively saying, "For each key/value pair in h, let the key iteration variable be the key, let the value iteration variable be the value, then execute the body of the loop."
If that doesn't help, have a look at this page on each... and if you can explain more about which bit you're finding tricky, we may be able to help more. (Well, others may be able to. I don't really know Ruby.)
The hash is indeed associated with these parameters because you call h.each to iterate over the hash:
h.each <- here's the link you are missing
Perhaps it's easier for you if you start with an array instead:
a = [1,2,3]
a.each do |v|
puts v
end
and play around with this first (each, each_with_index, ...)
when you call h.each, that's when you say that this is this specific h hash that you want to use for this each iteration.
Hence when you do that the value and key variables are assigned to the values in your hash, one by one.
I think the question is about the variable names. The names have no significance. Only the order matters. Within |...| inside each {...}, the key and the value are given in that order. Since its natural to assign the variable name key to key and value to value, you often find it done like that. In fact, it can be anything else.
each{|k, v| ...} # k is key, v is value
each{|a, b| ...} # a is key, b is value
or even misleadingly:
each{|value, key| ...} # value is key, key is value

Resources