Appending to list in ruby - ruby

I am totally newbie in ruby, I want to create a list depending on the values of the stage, for this example. I have assigned the constant values. I am getting an empty array (NIL value).
PROD_WAVE1_STAGE = "prod-wave1"
PROD_WAVE2_STAGE = "prod-wave2"
PROD_WAVE3_STAGE = "prod-wave3"
def prod_dimensionality stage
whitelist = []
case stage
when 'prod-wave1'
whitelist << 'NRT'
when 'PROD_WAVE2_STAGE'
whitelist << 'SIN'
when 'PROD_WAVE3_STAGE'
whitelist << 'DUB'
when 'PROD_WAVE4_STAGE'
whitelist << 'IAD'
end
end
prod_dimensionality(PROD_WAVE1_STAGE)

While the program as written is not wrong, it is a bit dangerous with respect to maintenance. Remember that a case statement returns the value of the last expression to be executed. In your case, this is something like whitelist << 'SIN', and since Array#<< returns the array itself, you are returning whitelist, and this is exactly what you need.
But imagine that for reason of debugging, you would add additional statements - for instance a test print - to your program, so it looks like this:
def prod_dimensionality(stage)
...
case ....
end
puts "whitelist=#{whitelist}" # Debugging output
end
In this case, the program would return the result of the puts statement, which will be nil. The caller would not see the whitelist anymore.
Therefore it is safer to write a return expression explicitly:
def prod_dimensionality(stage)
...
case ....
end
whitelist # This is what will be returned
end

You could make a hash that has the mappings in it. Then use that to decide what gets mapped in
stage_mappings = { 'prod-wave1' => 'NRT', ... }
whitelist << stage_mappings[stage]
whitelist.compact # In case there's some nils in there :D

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

why return change variables while inside a class

I cannot understand this ruby behavior, the code explains better what I mean:
class DoNotUnderstand
def initialize
#tiny_array = [3,4]
test
end
def messing(ary)
return [ary[0]+=700, ary[1]+=999]
end
def test
puts #tiny_array.join(",") # before => 3,4
messing(#tiny_array)
puts #tiny_array.join(",") # after => 703,1003
end
end
question = DoNotUnderstand.new
#tiny_array was [3,4] and became [703,1003]
if I don't use a class, that happens:
#tiny = [1,2]
def messing(ary)
return [ary[0]+693,ary[1]+999]
end
puts #tiny.join(",") # before => 1,2
messing(#tiny)
puts #tiny.join(",") # after => 1,2
the array simply remains [1,2]
why?
The class is a red herring, and completely irrelevant to the issue.
In the first case, where the array was modified, you defined messing as:
def messing(ary)
return [ary[0]+=700, ary[1]+=999]
end
Whereas in the second case, where the array was not modified, you defined messing as:
def messing(ary)
return [ary[0]+693,ary[1]+999]
end
In one case you used +=, and in the other, you used merely +.
ary[0] += 700 is exactly equivalent to ary[0] = ary[0] + 700. In other words you are changing the value stored in the 0th index of ary.
In the second case you merely add to the values stored in the array and return the result, but in the first case you not only return the result, you also store it back in the array.
For an explanation of why modifying ary modifies #tiny_array, see this answer to the question Is Ruby pass by reference or by value?.
You're second code example (the one from outside the class) is missing the two characters in the first that make it work the way it does. In the first example, the += operator is used, modifying the array in place:
return [ary[0]+=700, ary[1]+=999]
In your second example, the + operator is used, leaving the array as is:
return [ary[0]+693,ary[1]+999]
If you change it use the += operator, it works the same way as the first code snippet.

Iterating over array without returning the array

I'm working on a module to format print output for the console. The problem I'm running into is when I call .each on an array that I have, it returns the array in the console. I need to control what gets returned in the console.
How can I iterate through an array without having the array returned to the console?
#values.each do |value| # The end result of this is being returned, do not want.
printf(#format,
value[0], value[1], value[2], value[3], value[4], value[5],
value[6]
)
end
Why not create a method for your desired behavior?
def print_each_value(values, format)
values.each do |value|
printf(format, value[0], value[1], value[2], value[3], value[4], value[5], value[6])
end
nil # Here you set what you would like to return to the console.
end
print_each_value(#values, #format)
Edit: Removed annotative variable.
If you're only interested in the output and not the intermediate array which the interactive Ruby will always display you have two options. The first is to pass in the value you want returned:
#values.each_with_object(nil) do |value, x|
# ...
end
Whatever you supply as the argument there will be what is returned.
The second is to return the strings and print those:
puts(
#values.collect do |value|
#format % values
end.join("\n")
)
This has the advantage of dramatically simplifying your call.
As a note, if you have an array and you want to pass it through as arguments then use the splat operator:
printf(#format, *values)
Add ; to the end.
#values.each do |value| # The end result of this is being returned, do not want.
printf(#format,
value[0], value[1], value[2], value[3], value[4], value[5],
value[6])
end;
You can see the explanation in this article.
If you chain multiple statements together in the interactive shell, only the output of the last command that was executed will be displayed to the screen
And basicaly ; in Ruby is used for chaining.
Try #values.cycle(1) do |value|
I wish there'd be a dedicated method for iterating for side-effects only that returns nothing tho.

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.

Implicit return values in Ruby

I am somewhat new to Ruby and although I find it to be a very intuitive language I am having some difficulty understanding how implicit return values behave.
I am working on a small program to grep Tomcat logs and generate pipe-delimited CSV files from the pertinent data. Here is a simplified example that I'm using to generate the lines from a log entry.
class LineMatcher
class << self
def match(line, regex)
output = ""
line.scan(regex).each do |matched|
output << matched.join("|") << "\n"
end
return output
end
end
end
puts LineMatcher.match("00:00:13,207 06/18 INFO stateLogger - TerminationRequest[accountId=AccountId#66679198[accountNumber=0951714636005,srNumber=20]",
/^(\d{2}:\d{2}:\d{2},\d{3}).*?(\d{2}\/\d{2}).*?\[accountNumber=(\d*?),srNumber=(\d*?)\]/)
When I run this code I get back the following, which is what is expected when explicitly returning the value of output.
00:00:13,207|06/18|0951714636005|20
However, if I change LineMatcher to the following and don't explicitly return output:
class LineMatcher
class << self
def match(line, regex)
output = ""
line.scan(regex).each do |matched|
output << matched.join("|") << "\n"
end
end
end
end
Then I get the following result:
00:00:13,207
06/18
0951714636005
20
Obviously, this is not the desired outcome. It feels like I should be able to get rid of the output variable, but it's unclear where the return value is coming from. Also, any other suggestions/improvements for readability are welcome.
Any statement in ruby returns the value of the last evaluated expression.
You need to know the implementation and the behavior of the most used method in order to exactly know how your program will act.
#each returns the collection you iterated on. That said, the following code will return the value of line.scan(regexp).
line.scan(regex).each do |matched|
output << matched.join("|") << "\n"
end
If you want to return the result of the execution, you can use map, which works as each but returns the modified collection.
class LineMatcher
class << self
def match(line, regex)
line.scan(regex).map do |matched|
matched.join("|")
end.join("\n") # remember the final join
end
end
end
There are several useful methods you can use depending on your very specific case. In this one you might want to use inject unless the number of results returned by scan is high (working on arrays then merging them is more efficient than working on a single string).
class LineMatcher
class << self
def match(line, regex)
line.scan(regex).inject("") do |output, matched|
output << matched.join("|") << "\n"
end
end
end
end
In ruby the return value of a method is the value returned by the last statement. You can opt to have an explicit return too.
In your example, the first snippet returns the string output. The second snippet however returns the value returned by the each method (which is now the last stmt), which turns out to be an array of matches.
irb(main):014:0> "StackOverflow Meta".scan(/[aeiou]\w/).each do |match|
irb(main):015:1* s << match
irb(main):016:1> end
=> ["ac", "er", "ow", "et"]
Update: However that still doesn't explain your output on a single line. I think it's a formatting error, it should print each of the matches on a different line because that's how puts prints an array. A little code can explain it better than me..
irb(main):003:0> one_to_three = (1..3).to_a
=> [1, 2, 3]
irb(main):004:0> puts one_to_three
1
2
3
=> nil
Personally I find your method with the explicit return more readable (in this case)

Resources