How to refactor two loops to use one method in Ruby - ruby

I have the following code, and would like the loop to only exist once, but my return statements are different for each:
def valid?
patterns.each do |pattern|
match_data = text.match(pattern)
if match_data
return true
end
end
false
end
def pattern_matched
patterns.each do |pattern|
match_data = text.match(pattern)
if match_data
return pattern.source
end
end
nil
end
I don't want to store anything in state as I want these functions to be pure and independent of each other.
How can I write a helper function that runs the loop but returns either true/false OR pattern/nil?

How about:
def valid?
!pattern_matched.nil?
end
def pattern_matched
patterns.detect { |pattern| text.match(pattern) }&.source
end
I also replaced each loop as for finding first matching element detect seems like a better choice.

Related

Ruby Enumerable#find returning mapped value

Does Ruby's Enumerable offer a better way to do the following?
output = things
.find { |thing| thing.expensive_transform.meets_condition? }
.expensive_transform
Enumerable#find is great for finding an element in an enumerable, but returns the original element, not the return value of the block, so any work done is lost.
Of course there are ugly ways of accomplishing this...
Side effects
def constly_find(things)
output = nil
things.each do |thing|
expensive_thing = thing.expensive_transform
if expensive_thing.meets_condition?
output = expensive_thing
break
end
end
output
end
Returning from a block
This is the alternative I'm trying to refactor
def costly_find(things)
things.each do |thing|
expensive_thing = thing.expensive_transform
return expensive_thing if expensive_thing.meets_condition?
end
nil
end
each.lazy.map.find
def costly_find(things)
things
.each
.lazy
.map(&:expensive_transform)
.find(&:meets_condition?)
end
Is there something better?
Of course there are ugly ways of accomplishing this...
If you had a cheap operation, you'd just use:
collection.map(&:operation).find(&:condition?)
To make Ruby call operation only "on a as-needed basis" (as the documentation says), you can simply prepend lazy:
collection.lazy.map(&:operation).find(&:condition?)
I don't think this is ugly at all—quite the contrary— it looks elegant to me.
Applied to your code:
def costly_find(things)
things.lazy.map(&:expensive_transform).find(&:meets_condition?)
end
I would be inclined to create an enumerator that generates values thing.expensive_transform and then make that the receiver for find with meets_condition? in find's block. For one, I like the way that reads.
Code
def costly_find(things)
Enumerator.new { |y| things.each { |thing| y << thing.expensive_transform } }.
find(&:meets_condition?)
end
Example
class Thing
attr_reader :value
def initialize(value)
#value = value
end
def expensive_transform
self.class.new(value*2)
end
def meets_condition?
value == 12
end
end
things = [1,3,6,4].map { |n| Thing.new(n) }
#=> [#<Thing:0x00000001e90b78 #value=1>, #<Thing:0x00000001e90b28 #value=3>,
# #<Thing:0x00000001e90ad8 #value=6>, #<Thing:0x00000001e90ab0 #value=4>]
costly_find(things)
#=> #<Thing:0x00000001e8a3b8 #value=12>
In the example I have assumed that expensive_things and things are instances of the same class, but if that is not the case the code would need to be modified in the obvious way.
I don't think there is a "obvious best general solution" for your problem, which is also simple to use. You have two procedures involved (expensive_transform and meets_condition?), and you also would need - if this were a library method to use - as a third parameter the value to return, if no transformed element meets the condition. You return nil in this case, but in a general solution, expensive_transform might also yield nil, and only the caller knows what unique value would indicate that the condition as not been met.
Hence, a possible solution within Enumerable would have the signature
class Enumerable
def find_transformed(default_return_value, transform_proc, condition_proc)
...
end
end
or something similar, so this is not particularily elegant either.
You could do it with a single block, if you agree to merge the semantics of the two procedures into one: You have only one procedure, which calculates the transformed value and tests it. If the test succeeds, it returns the transformed value, and if it fails, it returns the default value:
class Enumerable
def find_by(default_value, &block)
result = default_value
each do |element|
result = block.call(element)
break if result != default_value
end
end
result
end
You would use it in your case like this:
my_collection.find_by(nil) do |el|
transformed_value = expensive_transform(el)
meets_condition?(transformed_value) ? transformed_value : nil
end
I'm not sure whether this is really intuitive to use...

Undefined method in console?

Here comes another Codecademy question:
The following challenge has been presented.
Define two methods in the editor:
A greeter method that takes a single string parameter, name, and
returns a string greeting that person. (Make sure to use return and
don't use print or puts.)
A by_three? method that takes a single integer parameter, number, and
returns true if that number is evenly divisible by three and false if
not. Remember, it's a Ruby best practice to end method names that
produce boolean values with a question mark.
The code I put in re: was..
def greeter(name)
return "Greet #{name}"
end
def by_three?(x)
if x % 3==0
returns true
else
return false
end
greeter("Brant")
by_three?(6)
The console then gives me the following error:
Did you define your greeter method?
It seems like I have. Am I wrong?
this would be it:
def greeter(name)
"Greet #{name}"
end
def by_three?(x)
x % 3 == 0
end
greeter("Brant") # => "Greet Brant"
by_three?(6) # => true
It looks like you did not add "end" after your else statement. Here you go.
#For the greeter method, i decided to use this format
def greeter(name)
return name
end
greeter("Hello Jane, good morning")
def by_three?(number)
if number % 3 != 1
return true
else
return false
end #Add end here to end your statement
end
by_three?(5)

is it OK to use ruby's implicit return value from []=

I am wandering if it is OK to use ruby's implicit return value when using []= method
[]= uses rb_hash_aset and it is returning val - http://www.ruby-doc.org/core-1.9.3/Hash.html#method-i-5B-5D-3D
here is a little code to demonstrate what I mean:
require 'benchmark'
CACHE = {}
def uncached_method(key)
warn "uncached"
rand(100)
end
def cached(key)
CACHE[key] || (CACHE[key] = uncached_method(key))
end
def longer_cached(key)
return CACHE[key] if CACHE[key]
CACHE[key] = uncached_method(key)
CACHE[key]
end
Benchmark.bm(7) do |x|
y = rand(10000)
cached(y)
x.report("shorter:") { 10000000.times do cached(y) end }
x.report("longer:") { 10000000.times do longer_cached(y) end }
end
of course longer_cached is slower because it does two hash lookups to return cached value, but when you read it line by line it makes more sense then the cached method.
I think using implicit returns is one of the things that makes ruby awesome, but I have always questioned their use when setting values.
So my question is: would you use implicit return from (hash[key] = val)?
You can also use the ||= operator in this case.
CACHE[key] ||= uncached_method(key)
This is a very common idiom.
Just because nobody mentioned it so far: you are not relying on the return value of Hash#[]=. That return value gets ignored anyway:
class ReturnFortyTwo
def []=(*)
return 42
end
end
r = ReturnFortyTwo.new
r[23] = 'This is the value that is going to be returned, not 42'
# => 'This is the value that is going to be returned, not 42'
In Ruby, assignment expressions always evaluate to the value that is being assigned. No exception. That is guaranteed by the Language Specification. So, I don't see anything wrong with relying on that.
In this case, shorter one is preferrable. Ussually = used in a subexpression is frowned upon, but here it's OK.
I'd keep it as simple and clean as possible (it's faster, too):
def cached(key)
value = CACHE[key]
unless value
value = uncached_method(key)
CACHE[key] = value
end
value
end

Use of yield and return in Ruby

Can anyone help me to figure out the the use of yield and return in Ruby. I'm a Ruby beginner, so simple examples are highly appreciated.
Thank you in advance!
The return statement works the same way that it works on other similar programming languages, it just returns from the method it is used on.
You can skip the call to return, since all methods in ruby always return the last statement. So you might find method like this:
def method
"hey there"
end
That's actually the same as doing something like:
def method
return "hey there"
end
The yield on the other hand, excecutes the block given as a parameter to the method. So you can have a method like this:
def method
puts "do somthing..."
yield
end
And then use it like this:
method do
puts "doing something"
end
The result of that, would be printing on screen the following 2 lines:
"do somthing..."
"doing something"
Hope that clears it up a bit. For more info on blocks, you can check out this link.
yield is used to call the block associated with the method. You do this by placing the block (basically just code in curly braces) after the method and its parameters, like so:
[1, 2, 3].each {|elem| puts elem}
return exits from the current method, and uses its "argument" as the return value, like so:
def hello
return :hello if some_test
puts "If it some_test returns false, then this message will be printed."
end
But note that you don't have to use the return keyword in any methods; Ruby will return the last statement evaluated if it encounters no returns. Thus these two are equivelent:
def explicit_return
# ...
return true
end
def implicit_return
# ...
true
end
Here's an example for yield:
# A simple iterator that operates on an array
def each_in(ary)
i = 0
until i >= ary.size
# Calls the block associated with this method and sends the arguments as block parameters.
# Automatically raises LocalJumpError if there is no block, so to make it safe, you can use block_given?
yield(ary[i])
i += 1
end
end
# Reverses an array
result = [] # This block is "tied" to the method
# | | |
# v v v
each_in([:duck, :duck, :duck, :GOOSE]) {|elem| result.insert(0, elem)}
result # => [:GOOSE, :duck, :duck, :duck]
And an example for return, which I will use to implement a method to see if a number is happy:
class Numeric
# Not the real meat of the program
def sum_of_squares
(to_s.split("").collect {|s| s.to_i ** 2}).inject(0) {|sum, i| sum + i}
end
def happy?(cache=[])
# If the number reaches 1, then it is happy.
return true if self == 1
# Can't be happy because we're starting to loop
return false if cache.include?(self)
# Ask the next number if it's happy, with self added to the list of seen numbers
# You don't actually need the return (it works without it); I just add it for symmetry
return sum_of_squares.happy?(cache << self)
end
end
24.happy? # => false
19.happy? # => true
2.happy? # => false
1.happy? # => true
# ... and so on ...
Hope this helps! :)
def cool
return yield
end
p cool {"yes!"}
The yield keyword instructs Ruby to execute the code in the block. In this example, the block returns the string "yes!". An explicit return statement was used in the cool() method, but this could have been implicit as well.

Refactoring respond_to? call in if-elsif-else condition

I have the following method and want to make it more readable:
def value_format(value)
if value.respond_to? :to_actor
value.to_actor
elsif value.respond_to? :to_subject
value.to_subject
elsif value.respond_to? :to_json
value.to_json
elsif value.respond_to? :to_hash
value.to_hash
else
value.inspect
end
end
This is my solution. What do you think?
def value_format(value)
methods = [:to_actor, :to_subject, :to_json, :to_hash, :inspect]
value.send(methods.find_all { |m| m if value.respond_to? m }.first)
end
Your solution looks fine, but you might as well use find instead of find_all:
METHODS = [:to_actor, :to_subject, :to_json, :to_hash, :inspect]
def value_format(value)
value.send(METHODS.find { |m| value.respond_to? m })
end
Using a constant has the advantage of not creating a new array every time value_format is ran.
Seems there's a pretty simple optimization to your solution:
def value_format(value)
methods = [:to_actor, :to_subject, :to_json, :to_hash]
value.send(methods.find(:inspect) { |m| value.respond_to? m })
end
The facets gem provides an elegant solution (I think) to this problem. It combines the two steps of checking if an object responds to a method and actually calling that method into a single step.
So your example could be rewritten as this:
require 'facets/kernel/respond'
def value_format(v)
v.respond.to_actor || v.respond.to_subject || v.respond.to_json || v.respond.to_hash || v.respond.inspect
end
Note that this method only works if it is safe to assume that none of these methods are going to return nil or false (because respond returns nil if the object doesn't respond, that is what allows us to chain it together with a bunch of ors).
Since all of the methods you listed should return strings, I believe this approach would work fine in your example.
Documentation:
# Like #respond_to? but returns the result of the call
# if it does indeed respond.
#
# class RespondExample
# def f; "f"; end
# end
#
# x = RespondExample.new
# x.respond(:f) #=> "f"
# x.respond(:g) #=> nil
#
# or
#
# x.respond.f #=> "f"
# x.respond.g #=> nil

Resources