Counter for parallel processes in Ruby - ruby-on-rails

I have this code that uses the parallel gem to split work among different processes.
Parallel.map(list, :in_processes=>4) do |item|
if item.name == "A"
puts "A"
else
puts "B"
end
end
What would be the best way to have a counter shared between the processes in order to get the exact number of times I got A and B?

Receive the Parallel's returned value (array of returned values from each iteration), and use Array#count method.
list = ["A","B","A","B","A","B","A","B","A","B"]
result = Parallel.map(list, :in_processes=>4) do |item|
if item == "A"
puts "Got A"
return "A"
else
puts "Got B"
return "B"
end
end
result # Array of "A" and "B" like ["A","B","A","A","B","B","A","A","B","B"]
# Now, you can do whatever you want with the result array.
count_a = result.count("A") # 5
count_b = result.count("B") # 5
(Ruby doesn't need explicit return keyword, but I put it to avoid any misunderstandings.)

Related

Checking if input == Integer/float? using irb

How to check if users didn't put an integer in if statement in a loop. I want the if statement to check if the user didn't put an integer, the system gonna say "please put an integer" Right now, everything works fine, but when user input a decimal number, the program will convert it to an integer. I want the program to say "you need to put an integer" when user put a decimal number.
I tried if soap.match(/^[\d]+$/).nil? and other methods, but didn't work. I think I need to change some code in 'if (soap_type = SOAPS[soap.to_i])', but I don't know how to change it.
SOAPS = {
1 => 'face soap',
2 => 'bar soap',
3 => 'shave soap',
4 => 'shampoo soap'
}.freeze
loop do
puts "What type of soaps do you want? (#{SOAPS.map { |k, v| "#{k} - #{v}" }.join(', ')}) Please put a number from 1 - 4 "
soap = gets
if (soap_type = SOAPS[soap.to_i])
puts "Great! You want #{soap_type}."
break
elsif !soap.match (/\A[-+]?[0-9]*\.?[0-9]+\Z/)
puts "You didn't enter an integer, please put an integer from 1-4"
else
puts "#{soap.inspect} is not a valid integer, please try again."
end
end
I hope when the user input a decimal number, the program gonna say You didn't enter an integer, please put an integer.
There's a much more Ruby way to express this, the idiomatic form if you will:
loop do
puts "What type of soaps do you want? (1 = face soap, 2 = bar soap, 3 = shave soap, 4 = shampoo soap) Please put a number from 1 - 4 "
case (soap = gets)
when '1'
puts "Great! You want face soap."
when '2'
puts "Great! You want bar soap"
when '3'
puts "Great! You want shave soap."
when '4'
puts "Great! You want shampoo soap."
else
puts "#{soap.inspect} is not a valid entry, please try again."
end
end
Where the case statement can do a ton of work for you. Here the string values are being tested instead of bothering to convert since that doesn't really matter.
Notice there's still a lot of duplication though. This is where a look-up table can be the key to simplifying things:
SOAPS = {
1 => 'face soap',
2 => 'bar soap',
3 => 'shave soap',
4 => 'shampoo soap'
}.freeze
loop do
puts "What type of soaps do you want? (#{SOAPS.map { |k, v| "#{k} - #{v}" }.join(', ')}) Please put a number from 1 - 4 "
soap = gets
if (soap_type = SOAPS[soap.to_i])
puts "Great! You want #{soap_type}."
else
puts "#{soap.inspect} is not a valid entry, please try again."
end
end
Where now you can easily add and remove entires and everything updates accordingly. This is a data-driven approach and it's what Ruby excels at because once you have something expressed in terms of data, you can build on that with a series of transformations to get the desired result.

Ruby: how to combine puts and return inside if condition?

How can I print a message and then return from a function in ruby?
2.3.4 :038 > def foo(num)
2.3.4 :039?> print "Your number is: #{num}" && return if num > 10
2.3.4 :040?> print "Number too small"
2.3.4 :041?> end
=> :foo
2.3.4 :042 > foo(47)
=> nil
2.3.4 :043 > foo(7)
Number too small => nil
2.3.4 :044 >
When I called foo with 47 why didn't I got Your number is: 47 in output?
PS: This function can be written in other simpler ways also, I just wanted to express my doubt via this function.
Because Ruby reads this line
print "Your number is: #{num}" && return if num > 10
like this
print("Your number is: #{num}" && return) if num > 10
That leads the method to return before it had the chance to print anything.
Adding a pair of parenthesis helps Ruby to resolve the desired order:
print("Your number is: #{num}") || return if num > 10
You have a precedence issue which can be fixed using parentheses. But since you are trying to express a control flow, you should use the control flow operators and and or instead of the boolean operators && and ||.
The idiom works like this: (just examples, it's not limited to return and fail)
do_something and return "it worked"
or:
do_something or fail "it didn't work"
It allows you to evaluate a second expression depending on whether the first expression succeeded or not. do_something is supposed to return a truthy value in case of success and a falsey value in case of failure.
print however always returns nil, so it doesn't work as expected in this regard. You would have to reverse the logic:
print "Your number is: #{num}" or return
But this doesn't read naturally any more, does it? Beside, return is always invoked, because print will never return a truthy value. You actually want:
print "Your number is: #{num}"
return
I would therefore simply write:
def foo(num)
if num > 10
puts "Your number is: #{num}"
else
puts "Number too small"
end
end
def foo(num)
puts(num > 10 ? "Your number is: #{num}" : "Number too small")
end
I think this is the cleaner way to do that, with an If ternary. Hope this helps
Since print (and puts) returns nil, this works just as well:
def foo(num)
return print "Your number is: #{num}" if num > 10
print "Number too small"
end
And to show clearer intention, place the guard clause first:
def foo(num)
return print "Number too small" if num <= 10
print "Your number is: #{num}"
end
Examples:
> foo 47
Your number is: 47=> nil
> foo 7
Number too small=> nil
Just out of curiosity:
def foo(num)
print case num
when -Float::INFINITY...10 then "Number too small"
else "Your number is: #{num}"
end
end
One more way to do one line if statements is with ;
def foo(num)
if num < 10; print "Your number is: #{num}" && return; end
print "Number too small"
end

Get the next item in a .each loop without ending the current item's iteration

I have a collection that I'm cycling through with an .each loop. Is there a way to get the next value without breaking the current iteration for a particular item in the collection?
collection = [foo, bar, quux]
collection.each do |item|
# Print the current iteration "foo"
p item #should return foo
# Also print the next iteration "bar"
# without breaking the current each loop for "foo"
p item.something_to_get_bar
end
collection.each_with_index do |item, i|
next_item = collection[i+1] # will be nil when past the end of the collection
end

creating dynamic hash with key and value in ruby on rails

I am trying to create Hash with dynamic key and respective values. For example like this
hash = {1 => 23.67, 1 => 78.44, 3 => 66.33, 12 => 44.2}
Something like this in which 1,2,12 are array index. I hope it is understandable. I am trying with the syntax from ROR tutorials.
Like this
test = Hash.new
for i in 0..23
if (s.duration.start.hour == array[i].hour)
s.sgs.each do |s1|
case s1.type.to_s
when 'M'
test ={i => s1.power} # here I am trying to create hash like give example in which i is for loop value
when 'L'
puts "to be done done"
else
puts "Not Found"
end
end
end
end
end
Updated code
test = Hash.new
for i in 0..23
if (s.duration.start.hour == array[i].hour)
s.sgs.each do |s1|
case s.type.to_s
when 'M'
puts s1.power;
test[i] = s1._power
when 'L'
puts "to be done"
else
puts "Not Found"
end
end
end
end
Results
on traversing
for t in 0..array.size
puts test[t]
end
Results :
t = 68.6 # which is last value
and expected
t = 33.4
t = 45.6 etc
Sample logs
after assign {23=>#<BigDecimal:7f3a1e9a6870,'0.3E2',9(18)>}
before assign {23=>#<BigDecimal:7f3a1e9a6870,'0.2E2',9(18)>}
after assign {23=>#<BigDecimal:7f3a1e9ce550,'-0.57E2',9(18)>}
before assign {23=>#<BigDecimal:7f3a1e9ce550,'-0.57E2',9(18)>}
if any other optimised solution is there would be good thanks
You are re-assigning test with a new hash on each iteration. You should add to it, so instead of
test ={i => s1.power}
you should do:
test[i] = s1.power
This sets the value of key i to s1.power
If you want to keep an array of all the values for a given key, I would suggest the following (more ruby-ish) solution:
hour_idx = array.find_index { |item| s.duration.start.hour == item.hour }
values = case s.type.to_s
when 'M'
s.sgs.map(&:_power)
when 'L'
puts "to be done"
else
puts "Not Found"
end
test = { hour_idx => values }
What I'm doing here is:
Find the hour_idx which is relevant to the current s (I assume there is only one such item)
Create an array of all the relevant values according to s.type (if it is 'M' an array of all the _power of s.sgs, for 'L' whatever map you need, and nil otherwise)
Create the target hash using the values set in #1 and #2...

Break out of Ruby Find.find loop when condition met

I'm iterating through a large directory of files with:
Find.find('/some/path/to/directory/root') do |path|
# ...do stuff with files…
end
What's a good way to limit the size of this enumerable group to 500 (or break out in some other way) if, say, Rails.env.development??
I could use a counter but what's "the Ruby way"?
This is one way:
Find.find('/some/path/to/directory/root').first(500).each do |path|
# do something
end
Find.find enumerates the files; In other words it wants to loop over all of them. You can tack on an index value using:
Find.find(ENV['HOME']).with_index(1) do |path, i|
puts path
break if i > 10
end
puts 'done'
and use that to keep track of how many you've processed.
If you want to "do 'em all":
limit = <some number>
do_em_all = (limit == 0)
Find.find(ENV['HOME']).with_index(1) do |path, i|
puts path
break unless (do_em_all || i <= limit)
end
puts 'done'
If limit is 0 you'll process everything. If it's > 0 you'll loop limit times.
You can also use Dir.entries or Dir.glob which will return an array of entries that you can slice apart as you want.
Enumerable's each_slice could be useful at that point.

Resources