Iterating over array without returning the array - ruby

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.

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

Appending to list in 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

Why isn't my print_linked_list_in_reverse function working?

One challenge in a Ruby course I'm doing is to print the :data values of the following linked list, in reverse:
{:data=>3, :next=>{:data=>2, :next=>{:data=>1, :next=>nil}}}
So when my method is passed the above code, it should return
1
2
3
Here's my attempt, which doesn't work for the above code. I can't figure out why, and I'd appreciate it if someone could explain what I'm doing wrong:
def print_list_in_reverse(hash)
if hash[:next].nil? #i.e. is this the final list element?
print "#{hash[:data]}\n"
return true
else
#as I understand it, the next line should run the method on `hash[:next]` as well as checking if it returns true.
print "#{hash[:data]}\n" if print_list_in_reverse(hash[:next])
end
end
Here's a solution, in case it helps you spot my mistake.
def print_list_in_reverse(list)
return unless list
print_list_in_reverse list[:next]
puts list[:data]
end
Thank you.
Your solution relies on return values, and you don't explicitly provide one in your else clause. In fact, you implicitly do because Ruby returns the result of the last statement evaluated, which for a print statement is nil. In Ruby false and nil are both logically false, causing the print to get bypassed for all but the last two calls. Your choices are to add a true at the end of the else, or make a solution that doesn't rely on return values.
To negate the need for return values, just check what logic is kosher based on info in the current invocation. You can simplify your life by leveraging the "truthiness" non-nil objects. Your basic recursive logic to get things in reverse is "print the stuff from the rest of my list, then print my stuff." A straightforward implementation based on truthiness would be:
def print_list_in_reverse(hash)
print_list_in_reverse(hash[:next]) if hash[:next]
print "#{hash[:data]}\n"
end
The problem with that is that you might have been handed an empty list, in which case you don't want to print anything. That's easy to check:
def print_list_in_reverse(hash)
print_list_in_reverse(hash[:next]) if hash[:next]
print "#{hash[:data]}\n" if hash
end
That will work as long as you get handed a hash, even if it's empty. If you're paranoid about being handed a nil:
def print_list_in_reverse(hash)
print_list_in_reverse(hash[:next]) if hash && hash[:next]
print "#{hash[:data]}\n" if hash
end
The other alternative is to start by checking if the current list element is nil and returning immediately in that case. Otherwise, follow the basic recursive logic outlined above. That results in the solution you provided.
Better to iterate over every value in your hash, and push the values until there's no any other hash as value inside the main hash.
def print_list_in_reverse(hash, results = [])
hash.each_value do |value|
if value.is_a? Hash
print_list_in_reverse(value, results)
else
results << value unless value.nil?
end
end
results.reverse
end
p print_list_in_reverse(data)
=> [1, 2, 3]
The problem in your code is in the else-case. You need to return true to print the hash[:data].
Your method always print the last 2 elements.

Using yield within a select statement

If I wanted to prune an array by a given set of parameters I would write something like this:
array = [4,5,6,7,8]
a = array.select{|i| i>=5}
puts a.inspect
which would return [5,6,7,8].
I want to write a function "filter" which accomplishes the same thing. In this case my first thought is to write something like:
array = [4,5,6,7,8]
a = filter(array) {|i| i >= 5}
puts a.inspect
What I can't figure out is how to properly call yield within the method to invoke the code block during the select statement:
a = array.select{yield}
Doesn't seem to work since it attempts to call the code block on nil, not the array within the function. What's the proper way of doing this?
Don't know if it makes sense for you, but try:
def filter(array)
array.select { |i| yield(i) }
end
array = [4,5,6,7,8]
p filter(array) {|i| i >= 5}
When you write code within braces {...} to be passed to a method, this code is called a block. It is normally passed implicitly to a method (i.e. it is not a named argument). To invoke this implicit block, you call yield.
In your case, you don't want to invoke the block yourself; you want your filter method to pass the block along to select, where the actual filtering takes place.
To "pass along a block", you can make the method's block argument explicit by using the & prefix. Note that the name block in this example is just convention; there is no special block keyword. The important part is the & character:
def filter(array, &block)
array.select(&block)
end
array = [4,5,6,7,8]
filter(array) { |i| i >= 5 } # => [5,6,7,8]

Iterate over array of arrays

This has been asked before, but I can't find an answer that works. I have the following code:
[[13,14,16,11],[22,23]].each do |key,value|
puts key
end
It should in theory print:
0
1
But instead it prints:
13
22
Why does ruby behave this way?
Why does ruby behave this way?
It's because what actually happens internally, when each and other iterators are used with a block instead of a lambda, is actually closer to this:
do |key, value, *rest|
puts key
end
Consider this code to illustrate:
p = proc do |key,value|
puts key
end
l = lambda do |key,value|
puts key
end
Using the above, the following will set (key, value) to (13, 14) and (22, 23) respectively, and the above-mentioned *rest as [16, 11] in the first case (with rest getting discarded):
[[13,14,16,11],[22,23]].each(&p)
In contrast, the following will spit an argument error, because the lambda (which is similar to a block except when it comes to arity considerations) will receive the full array as an argument (without any *rest as above, since the number of arguments is strictly enforced):
[[13,14,16,11],[22,23]].each(&l) # wrong number of arguments (1 for 2)
To get the index in your case, you'll want each_with_index as highlighted in the other answers.
Related discussions:
Proc.arity vs Lambda.arity
Why does Hash#select and Hash#reject pass a key to a unary block?
You can get what you want with Array's each_index' method which returns the index of the element instead of the element itself. See [Ruby'sArray` documentation]1 for more information.
When you do:
[[13,14,16,11],[22,23]].each do |key,value|
before the first iteration is done it makes an assignment:
key, value = [13,14,16,11]
Such an assignment will result with key being 13 and value being 14. Instead you should use each_with_index do |array, index|. This will change the assignment to:
array, index = [[13,14,16,11], 0]
Which will result with array being [13,14,16,11] and index being 0
You have an array of arrays - known as a two-dimensional array.
In your loop, your "value" variable is assigned to the first array, [13,14,16,11]
When you attempt to puts the "value" variable, it only returns the first element, 13.
Try changing puts value to puts value.to_s which will convert the array to a string.
If you want every value, then add another loop block to your code, to loop through each element within the "value" variable.
[[1,2,3],['a','b','c']].each do |key,value|
value.each do |key2,value2|
puts value2
end
end

Resources