Example(Rails):
def blabla
#ads = ["1", "2"] if #ads.nil?
#reklame = Reklamer.find(#koder.sample)
#ads[0] = #reklame.id
if #ads[0] == #ads[1]
begin from start method from start
end
end
Is there a method to restart the method/action so it begins from the top?
Try the keyword retry, this keyword will restart the iteration from the beginning so consider to rebuild your method to use iteration, for example like this:
def blabla
#ads = ["1", "2"] if #ads.nil?
#ads.each_slice do |prev_,next_|
#reklame = Reklamer.find(#koder.sample)
prev_ = #reklame.id
retry if prev_ == next_
end
end
Also the keyword redo will repeat current iteration.
You could use ruby-goto but I would definitely recommend against it.
For more information on why not to use goto statements check out this question. It is C# based but I think it covers the point nicely.
Not sure what you want to accomplish, but it sounds like you need a loop of some sort. Would something like this do what you need?
def blabla
#ads ||= ["1", "1"]
# careful with comparing a string ("2") to a numeric id
while #ads[0] == #ads[1] do
#reklame = Reklamer.find(#koder.sample)
#ads[0] = #reklame.id
end
end
Basically you keep reseting #ads[0] until it is different from #ads[1].
Related
Using interpolated strings is it possible to call multiple #{} within each other?
For example I want to create a variable and add a number onto the end. I also want to attach a result from a column in site, but also increment the number as well. What the best method of doing the below code?
#site.each do |site|
1.upto(4) do |i|
eval("#var#{i} = #{site.site_#{i}_location}", b)
end
end
So #var1 = site.site_1_location, #var2 = site.site_2_location, etc.
Mocking #sites data:
#sites = [OpenStruct.new(
site_1_location: 'Japan',
site_2_location: 'India',
site_3_location: 'Chile',
site_4_location: 'Singapore'
)]
You can use instance_variable_set to set the instance variable
#sites.each do |site|
1.upto(4) do |i|
instance_variable_set("#var#{i}", site.send("site_#{i}_location"))
end
end
Now you can access the variables:
#var1 # returns "Japan"
#var2 # returns "India"
#var3 # returns "Chile"
#var4 # returns "Singapore"
Using #send might help
code = "#var#{i} = site.send(:site_#{i}_location)"
eval(code)
Yes of cause, nesting of #{} is possible.
Stupid example, but it shows the possibilties of nesting:
x = 1
y = 2
"abc_#{"#{x}_#{y if y > 1}"}"
# => 'abc_1_2'
Never the less, the solution for your code, suggested by Imram Ahmad (https://stackoverflow.com/a/66002619/14485176) is the better aproach to solve your problem!
I am trying to write fast and concise code. I'd appreciate your thoughts on which is the best way to write the following code and why:
Option #1
def get_title
title = check_in_place_one
if title.empty?
title = check_in_place_two
if title.empty?
title = check_in_place_three
end
end
return title
end
Option #2
def get_title
title = check_in_place_one
title = check_in_place_two unless !title.empty?
title = check_in_place_three unless !title.empty?
return title
end
I think Option #1 is better since if the title is found by check_in_place_one, we test title.empty? once and then skip the rest of the code in the method and return. But, it looks too long. Option #2 appears better, but processes title.empty? one extra time, and unnecessary time before returning. Also, am I missing a third option?
From performance, there is no difference between the two versions of your code (besides very minor difference that may come from parsing, which should be ignorable). The control structures are the same.
From readability, if you can get away with nesting, doing so is better. Your second option is better.
It is usually better to get rid of any case that does not need further processing. That is done by return.
def get_title
title = check_in_place_one
return title unless title.empty?
title = check_in_place_two
return title unless title.empty?
title = check_in_place_three
return title
end
The last title = and return in the code above are redundant, but I put them there for consistency, which improves readability.
You can further compact the code using tap like this:
def get_title
check_in_place_one.tap{|s| return s unless s.empty?}
check_in_place_two.tap{|s| return s unless s.empty?}
check_in_place_three
end
tap is a pretty much fast method, and unlike instance_eval, its performance penalty is usually ignorable.
The following approach could be used for any number of sequential tests. Moreover, it is completely general. The return condition could be changed, arguments could easily be assigned to the test methods, etc.
tests = %w[check_in_place_one check_in_place_two check_in_place_three]
def do_tests(tests)
title = nil # Define title outside block
tests.each do |t|
title = send(t)
break unless title.empty?
end
title
end
Let's try it:
def check_in_place_one
puts "check 1"
[]
end
def check_in_place_two
puts "check 2"
''
end
def check_in_place_three
puts "check 3"
[3]
end
do_tests(tests) #=> [3]
check 1
check 2
check 3
#=> [3]
Now change one of the tests:
def check_in_place_two
puts "check 2"
'cat'
end
do_tests(tests) #=> 'cat'
check 1
check 2
#=> "cat"
If there were more tests, it might be convenient to put them in a module which would be included into a class. Mixed-in methods behave the same as those that you define for the class. For example, they have access to instance variables. I will demonstrate that with the definition of the first test method. We probably want to make the test methods private. We could do it like this:
module TestMethods
private
def check_in_place_one
puts "#pet => #{#pet}"
puts "check 1"
[]
end
def check_in_place_two
puts "check 2"
''
end
def check_in_place_three
puts "check 3"
[3]
end
end
class MyClass
##tests = TestMethods.private_instance_methods(false)
puts "##tests = #{##tests}"
def initialize
#pet = 'dog'
end
def do_tests
title = nil # Define title outside block
##tests.each do |t|
title = send(t)
break unless title.empty?
end
title
end
include TestMethods
end
The following is displayed when the code is parsed:
##tests = [:check_in_place_one, :check_in_place_two, :check_in_place_three]
Now we perform the tests:
MyClass.new.do_tests #=> [3]
#pet => dog
check 1
check 2
check 3
Confirm the test methods are private:
MyClass.new.check_in_place_one
#=> private method 'check_in_place_one' called for...'
The advantage of using a module is that you can add, delete, rearrange and rename the test methods without making any changes to the class.
Well, here's a few other alternatives.
Option 1: Return first non-empty check.
def get_title
return check_in_place_one unless check_in_place_one.empty?
return check_in_place_two unless check_in_place_two.empty?
return check_in_place_three
end
Option 2: Helper method with short-circuit evaluation.
def get_title
check_place("one") || check_place("two") || check_place("three")
end
private
def check_place(place)
result = send("check_in_place_#{place}")
result.empty? ? nil : result
end
Option 3: Check all places then find the first that's non-empty.
def get_title
[
check_in_place_one,
check_in_place_two,
check_in_place_three,
].find{|x| !x.empty? }
end
Option 2 looks good although you did a 360 degree turn with the unless !title.empty?. You can shorten that to if title.empty? since unless is equivalent to an if ! so doing an unless ! takes you back to just if.
If you're only ever going to have 3 places to look in then option 2 is the best. It's short, concise, and easy to read (easier once you fix the aforementioned whoopsie). If you might add on to the places you look for a title in you can get a bit more dynamic:
def get_title(num_places = 4)
title, cur_place = nil, 0
title = check_in_place(cur_place += 1) while title.nil? && cur_place < num_places
end
def check_in_place(place_num)
# some code to check in the place # given by place_num
end
The tricky line is that one with the while in it. What's happening is that the while will check the expression title.nil? && cur_place < num_places and return true because the title is still nil and 0 is less than 4.
Then we'll call the check_in_place method and pass in a value of 1 because the cur_place += 1 expression will increment its value to 1 and then return it, giving it to the method (assuming we want to start checking in place 1, of course).
This will repeat until either check_in_place returns a non nil value, or we run out of places to check.
Now the get_title method is shorter and will automatically support checking in num_places places given that your check_in_place method can also look in more places.
One more thing, you might like to give https://codereview.stackexchange.com/ a look, this question seems like it'd be a good fit for it.
I don't think there's any reason to get too clever:
def get_title
check_in_place_one || check_in_place_two || check_in_place_three
end
Edit: if the check_in_place_X methods are indeed returning an empty string on failure it would be better (and more idiomatic) to have them instead return nil. Not only does it allow for truthy comparisons like the above code, return "" results in the construction of a new and unnecessary String object.
I have the following code
# colours a random cell with a correct colour
def colour_random!
while true do
col, row = rand(columns), rand(rows)
cell = self[row,col]
if cell.empty? then
cell.should_be_filled? ? cell.colour!(1) : cell.colour!(0)
break
end
end
end
it's not that important what's doing, although it should pretty obvious. The point is that Rubocop gives me a warning
Never use 'do' with multi-line 'while
Why should I not do that? How should I do it then?
while is a keyword,so you don't need to pass a block. Without do..end it will work fine. The below is fine
def colour_random!
while true
col, row = rand(columns), rand(rows)
cell = self[row,col]
if cell.empty? then
cell.should_be_filled? ? cell.colour!(1) : cell.colour!(0)
break
end
end
end
while is a keyword, and if you pass a block to it, like do..end, it still works as you asked it to do, by not throwing any error, rather just a warning. But it could be dangerous if you try to pass a Proc or Method object to it, and dynamically try to convert it to a block using & keyword, as we do generally. That means
# below code will work as expected just throwing an warning.
x = 2
while x < 2 do
#code
end
But if you try to do by mistake like below
while &block # booom!! error
The reason is while is a keyword, which don't support any to_proc method to satisfy your need. So it can be dangerous.
Ruby style guide also suggested that Never use while/until condition do for multi-line while/until
I think the reason is as Nobuyoshi Nakada said in the mailing list
loop is a kernel method which takes a block. A block introduces new local variable scope.
loop do
a = 1
break
end
p a #=> causes NameError
while doesn't.
while 1
a = 1
break
end
p a #=> 1
Ruby actually has a shortcut for while true: the loop statement.
def colour_random!
loop do
col, row = rand(columns), rand(rows)
cell = self[row,col]
if cell.empty? then
cell.should_be_filled? ? cell.colour!(1) : cell.colour!(0)
break
end
end
end
taglist = [{:name=>"Daniel_Xu_Forever", :tag=>["helo", "world"]},
{:name=>"kcuf", :tag=>["hhe"]},
{:name=>"fine", :tag=>[]},
{:name=>"how hare you", :tag=>[]},
{:name=>"heki", :tag=>["1", "2", "3"]},
{:name=>"railsgirls", :tag=>[]},
{:name=>"_byoy", :tag=>[]},
{:name=>"ajha", :tag=>[]},
{:name=>"nimei", :tag=>[]}]
How to get specified name's tag from taglist
For example , I want to extract user "fine"'s tag?
Could this be achieved without do iterator?
This will return the contents of the :tag key for any users name which == 'fine'
taglist.select { |x| x[:name] == 'fine' }.map { |u| u[:tag] }
First you select out only the users you are interested with .select.
And then use .map to collect an array of only what you want.
In this case the end result will be: []
Is do really an iterator?
taglist.find{|tl| tl[:name] == 'fine'}[:tag]
Just to be silly how about:
eval taglist.to_s[/:name=>"fine", :tag=>(.*?)}/, 1]
#=> []
No, it cannot be done without a loop.
And even if you find a solution where your code avoids a loop, for sure the library function that you're calling will include a loop. Finding an element in an array requires a loop. Period.
For example, take this (contrived) example
pattern = "fine"
def pattern.===(h); self == h[:name]; end
taglist.grep(pattern)
which does not seem to use a loop, but calls grep which is implemented using a loop.
Or another, equally contrived, example
class Hash; def method_missing(sym); self[sym]; end; end
taglist.group_by(&:name)["fine"]
which again does seem to call group_by without a loop, but actually it does.
So the answer is, no.
So my first answer missed the no do rule.
Here is an answer that doesn't use a do block.
i=0
begin
if taglist[i][:name] == 'fine'
tag = taglist[i][:tag]
break
end
i+=1
end while i < taglist.length - 0
Technically I think this is still using a block. But probably satisfies the restriction.
Let's say I'm doing a simple .each but I still want to keep the position in the loop, I can do:
i = 0
poneys.each do |poney|
#something involving i
#something involving poney
i = i + 1
end
This doesn't look very elegant to me. So I guess I could get rid of the .each:
for i in 0..poneys.size-1 do
#something involving i
end
... or something similar with a different syntax.
The problem is that if I want to access the object I have to do:
for i in 0..poneys.size-1 do
poney = poneys[i]
#something involving i
#something involving poney
end
... and that's not very elegant either.
Is there a nice and clean way of doing this ?
You can use Enumerable#each_with_index
From the official documentation:
Calls block with two arguments, the
item and its index, for each item in
enum.
hash = Hash.new
%w(cat dog wombat).each_with_index do |item, index|
hash[item] = index
end
hash #=> {"cat"=>0, "wombat"=>2, "dog"=>1}
Depends what do you do with poneys :) Enumerable#inject is also a nice one for such things:
poneys.inject(0) do |i, poney|
i += 1; i
end
I learned a lot about inject from http://blog.jayfields.com/2008/03/ruby-inject.html which is great article.