One line block and if condition - ruby

I have:
foos.each do |foo|
unless foo
puts "Foo is missing"
next
end
# rest of business logic goes here
end
I would like to write the last part of it better, something like
{ puts "Foo is missing"; next } unless foo
Unfortunately, this does not work. Does anybody know a way to write two (blocks of) commands inline with if condition?

Just use parentheses:
(puts 'a'; puts 'b') if true
#=> a
#=> b

What you are looking for can be done with parentheses:
(puts "Foo is missing"; next) unless foo
But in this particular case, it is better to write:
next puts "Foo is missing" unless foo

Use begin..end block:
begin puts "Foo is missing"; next end unless foo

foos.each { |foo| foo or ( puts "Foo is missing"; next )
# the rest of the business logic goes here
}

You can use the or syntax
[1,2,3].each do |x|
puts 'two' or next if x == 2
puts x
end
#=> 1
#=> "two"
#=> 3

Related

Different behaviour of 'do .. end' and "{..}" block in ruby [duplicate]

This question already has answers here:
What is the difference or value of these block coding styles in Ruby?
(7 answers)
Closed 6 years ago.
Sorry if this question is duplicate. But I can't find the difference in usage. When I run the below codes I am getting different answers. I see from most tutorials that using of "do ... end" is same as "{ ... }" blocks.
include Comparable
a = [1, 4, 2, 3, 5]
p a.sort do |x,y|
y <=> x
end
output shows as = [1, 2, 3, 4, 5]
but when I run like this...
include Comparable
a = [1, 4, 2, 3, 5]
p a.sort { |x,y|
y <=> x
}
output shows as = [5, 4, 3, 2, 1]
what was wrong here. is there any situations that two syntaxes has any different behaviour?
The precedence is different. The first one is interpreted as
p (a.sort) do
...
end
Since the block is not passed to sort, it is sorted in the default, ascending order. Then, the block is passed to p, which does not use it.
sawa's answer is correct, but as the OP has asked for more clarification, I'm supplying my own answer.
All four of these method calls behave the same, passing a block to a foo method:
foo { ... }
foo do ... end
foo() { ... }
foo() do ... end
When you write two methods and a block, without parentheses around the arguments, it is unclear which method the block goes with:
foo bar { ... }
foo bar do ... end
The question is: "Am I passing a block to bar, and then passing its return value to foo? Or am I calling foo with bar as an argument and also passing along a block?"
With parentheses, you can make this clear using either block style:
# Passing a block to `bar`, and then passing the result to `foo`
foo( bar { ... } )
foo( bar do ... end )
# Passing an argument and block to `foo`
foo( bar ) { ... }
foo( bar ) do ... end
The difference between {…} and do…end that you have run into is where Ruby chooses to put the parentheses when you omit them. The two block notations have different precedence, and so you end with different results:
# Passing a block to `bar`, and then passing the result to `foo`
foo bar{ ... }
foo( bar do ... end )
# Passing an argument and block to `foo`
foo bar do ... end
foo( bar ){ ... }
So, specifically in your case:
# This code…
p a.sort do |x,y|
y <=> x
end
# …is the same as this code:
b = a.sort
p(b){ |x,y| y <=> x }
# Note that you are passing a block to the `p` method
# but it doesn't do anything with it. Thus, it is
# functionally equivalent to just:
p a.sort
And,
# This code…
p a.sort { |x,y|
y <=> x
}
# …is functionally the same as this code:
b = a.sort{ |x,y| y <=> x }
p b
Finally, if you still don't get it, perhaps deeply considering the following code and output will help:
def foo(a=nil)
yield :foo if block_given?
end
def bar
yield :bar if block_given?
:bar_result
end
foo bar { |m| puts "block sent to #{m}" }
#=> block sent to bar
#=> foo got :bar_result
foo( bar do |m| puts "block sent to #{m}" end )
#=> block sent to bar
#=> foo got :bar_result
foo( bar ){ |m| puts "block sent to #{m}" }
#=> foo got :bar_result
#=> block sent to foo
foo bar do |m| puts "block sent to #{m}" end
#=> foo got :bar_result
#=> block sent to foo
Notice that the first and last examples in the code immediately above differ only in whether they use {…} or do…end for the block.
As others have pointed out, there is a difference in precedence between the two types of blocks. However, the precedence does not really influence how it's used in practice.
Some Rubyists follow the Weirich Convention, named so by Avdi Grimm.
In short, the use of one over the other is based on whether it's functional (return value is used), in which case { ... } is used, or procedural (changes the state of the system in some way or performs output), in which case do ... end is used.

Is there a way to group a block of statements?

I am working on some refactoring tool. It would be great if I can replace some method call in place by its definition, which would generally be a block of statements. For example, the original code may be:
some_condition ? a : b
def a
...
# statements1
...
end
def d
...
# statements2
...
end
and I want my inlining tool to replace the method call by the blocks of code directly.
To do that, I want to group a list of statements together. How do we usually do that? Is there a way of writing code like this?
some_condition ? {
...
# statements1
...
} : {
...
# statements2
...
}
Yes. You can either use parentheses, or begin...end.
true ? (
puts "a"
puts "b"
puts "c"
) : (
puts "d"
)
true ? begin
puts "a"
puts "b"
puts "c"
end : begin
puts "d"
end
Your code can be reduced to just one line.
%w(a b c).each { |char| puts char }
The else statement is irrelevant here, because true will always return true.

Capitalize Second Word in a String (ruby)

I'm trying to create a method to capitalize the second word in a string. The code below works, but I was wondering if there are other ways to do this:
def camelcase(string)
tmp = string.split
tmp[1].capitalize!
tmp.join('')
end
def camelcase(string)
string.gsub(/\s(\w)/) { |match| $1.capitalize }
end
camelcase("foo bar baz") #=> "fooBarBaz"
Or you might wanna take a look at the camelcasemethod that comes with ActiveSupport::Inflector (see: http://apidock.com/rails/String/camelize)
def camelcase(string)
string.sub(/\s.*/) { |s| s.delete(' ').capitalize}
end
puts camelcase("foo bar bas")
=> "fooBarbaz"
You could use tap which "Yields x to the block, and then returns x" according to the docs. In this case capitalize! modifies x in place before being returned to the method chain for further processing by join.
def camelcase(string)
string.split.tap { |words| words[1].capitalize! }.join
end
camelcase('foo bar baz')
=> "fooBarbaz"
Try this:
s = "foo bar"
s.sub(/\s(\w)/) { $1.capitalize } # => "fooBar"

Return a single value from a nested each block, trying to use 'return'

def get_type
x = [{:type=>'A', :patterns=>['foo.*']}, {:type=>'B', :patterns=>['bar.*']}]
name = 'foo.txt'
result = x.each { |item|
item[:patterns].each { |regex|
puts "Checking #{regex} against #{name}"
if !name.match(regex).nil?
puts "Found match: #{item[:type]}"
return item[:type]
end
}
}
end
result = get_type
puts "result: #{result}"
Expected output:
Checking foo.* against foo.txt
Found match: A
result: A
However, all I see is:
Checking foo.* against foo.txt
Found match: A
My current work around is this:
def get_type
x = [{:type=>'A', :patterns=>['foo.*']}, {:type=>'B', :patterns=>['bar.*']}]
name = 'foo.txt'
result = []
x.each { |item|
item[:patterns].each { |regex|
puts "Checking #{regex} against #{name}"
if !name.match(regex).nil?
puts "Found match: #{item[:type]}"
result << item[:type]
end
}
}
result[0] unless result.empty?
end
Why doesn't the first approach work? or maybe it is 'working', I just don't understand why I'm not getting what I'd expect.
May I suggest a refactor? your code looks kind of clunky because you are using each loops (imperative) when you in fact need a map+first (functional). As Ruby enumerables are not lazy this would be inefficient, so people usually build the abstraction Enumerable#map_detect (or find_yield, or find_first, or map_first):
def get_type_using_map_detect(name)
xs = [{:type => 'A', :patterns => ['foo.*']}, {:type => 'B', :patterns => ['bar.*']}]
xs.map_detect do |item|
item[:patterns].map_detect do |regex|
item[:type] if name.match(regex)
end
end
end
This is a possible implementation of the method:
module Enumerable
# Like Enumerable#map but return only the first non-nil value
def map_detect
self.each do |item|
if result = (yield item)
return result
end
end
nil
end
end
Works fine for me. Are you actually invoking it with
result = get_type puts "result: #{result}"
? Because that shouldn't work at all, though I'm assuming there's a linefeed that got eaten when you posted this.

Is there a ruby method that just returns the value of a block?

I know about Object#tap, which takes a value and returns that value. But is there a method that takes a block and returns the value evaluated by the block?
To improve my code in this answer (which is more complicated than the snippet below), I'd like to change
deck.index("A").tap {|index|
STDERR.puts "Result of indexing for #{"A".inspect} is #{index.inspect}"
}
, which has "A" repeated, into
def my_method(*args)
yield *args
end
deck = ['A', 'B', 'C']
my_method("A") {|value| deck.index(value).tap {|index|
STDERR.puts "Result of indexing for #{value.inspect} is #{index.inspect}"
} }
# Result of indexing for "A" is 0
# => 0
What you're looking for is essentially the equivalent of let in Lisp or OCaml — something that allows you to temporarily bind a value to an identifier without introducing a new variable into the larger scope. There isn't anything that lets you do such a thing with that syntax in Ruby. The equivalent Ruby would be:
lambda {|value| deck.index(value).tap {|index|
STDERR.puts "Result of indexing for #{value.inspect} is #{index.inspect}"
} }.call 'A'
You could of course just write a method like:
def let(*values)
yield *values
end
I think you could solve it with fibers. Something like:
def myfiber
block = lambda{nil}
loop{ block = Fiber.yield(block.call) }
end
f = Fiber.new {myfiber }
f.resume
puts "result: #{f.resume(lambda{1})}"
puts "result: #{f.resume(lambda{5})}"
puts "result: #{f.resume(lambda{2})}"
will result in:
result: 1
result: 5
result: 2

Resources