Ruby custom method chaining - ruby-on-rails

What is the best way of chaining multiple custom methods together? I want to put the output of my method directly into the next method in an elegant way. The first one below is what I have now.
verified = search_verified(#providers)
matching = search_matching(verified)
deactivated = search_deactivated(matching)
networks = search_networks(deactivated)
designations = search_designations(networks)
disorders = search_disorders(designations)
age_groups = search_age_groups(disorders)
governing = search_governing(age_groups)
search_availabilities(governing)
maybe something more along the lines of:
search_verified(#providers)
>> search_matching
>> search_deactivated
>> search_networks
>> ....

You might want to use then to chain your methods and numbers parameter to simplify the blocks:
search_verified(#providers)
.then { search_matching(_1) }
.then { search_deactivated(_1) }
.then { search_networks(_1) }
.then { search_designations(_1) }
.then { search_disorders(_1) }
.then { search_age_groups(_1) }
.then { search_governing(_1) }
.then { search_availabilities(_1) }

You can do something along those lines. Ruby 2.6.0 introduces then, and the function composition operators << and >>.
You do have to select your methods with method(:method_name) because method_name by itself invokes the method and does not return it.
#providers.then(&
method(:search_verified) >>
method(:search_matching) >>
method(:search_deactivated) >>
method(:search_networks) >>
method(:search_designations) >>
method(:search_disorders) >>
method(:search_age_groups) >>
method(:search_governing) >>
method(:search_availabilities)
)
If you don't like the character "overhead". You could shrink the amount of characters by storing the method method in a shorter variable first:
fn = method(:method)
#providers.then(&
fn[:search_verified] >>
# ...
)

You could write that as follows.
METHODS = [:verified, :matching, :deactivated, :networks, :designations,
:disorders, :age_groups, :governing, :search_availabilities]
def doit(first_arg)
METHODS.reduce(first_arg) { |arg, meth| send(meth, arg) }
end
This would be called
doit(#providers)
For example:
def a(arg)
arg * 2
end
def b(arg)
arg + 1
end
def c(arg)
arg/5.0
end
METHODS = [:a, :b, :c]
doit(3)
#=> 1.4
One could alternatively write
def doit(first_arg)
METHODS.reduce(first_arg) { |arg, meth| method(meth).call(arg) }
end
doit(3)
#=> 1.4
One advantage of this approach is that if methods are added, removed or renamed is is only necessary to change the constant METHODS; the method doit is unaffected.

I've not actually tried this other than a quick attempt in the console, but from looking at: https://andersmurphy.com/2019/12/07/ruby-functional-programming.html it looks like something like the following should be possible:
pipe = -> *fns {fns.reverse.reduce {|f, g| -> x {f.(g.(x))}}}
add_one = -> x {x + 1}
times_two = -> x {x * 2}
add_one_and_times_two = pipe.(add_one, times_two)
add_one_and_times_two.(2)
=> 6
pipe.(add_one, times_two).(3)
=> 8
If you want to use this with methods you can possibly (this seems to work in the console) do something like:
def divide_by_three(x); x / 3.0 end
pipe.(
add_one,
times_two,
method(:divide_by_three)
).(4)
=> 3.3333333333333335
using the method function as shown in #3limin4t0r's answer.

If all the methods are in one class you can chain these methods by returning self in each method.
For the sake of clarity I take your example but the providers are just numbers.
class MyClass
def initialize
##providers = [2, 6, 4, 8, 7]
end
def search_matching
# do stuff
##providers.select!{ |n| n > 3 }
self
end
def search_deactivated
# do other stuff
##providers.select!{ |n| n < 8 }
self
end
def providers
##providers
end
end
MyClass.new.search_matching.search_deactivated.providers # [6, 4, 7]

it depends on what data do you really need and how you define the architecture of your code, i usually make a Service object.
if you only want to return only the last method output. return search_availabilities(governing).
If you need all variables, you can make it return an array with all the variables or the ones that you need. return [verified, matching, deactivated, ...].

Related

How to trasform all values in a nested hash?

I want to convert all the values in a nested hash to a utf8 compatible string. I initially thought this would be easy and something like deep_apply should be available for me to use, but I am unable to find anything this simple on a quick google and SO search.
I do not want to write (maintain) a method similar to the lines of Change values in a nested hash . Is there a native API implementation or a shorthand available for this or do I have to write my own method?
I ended up implementing my own approach, that is in no way perfect but works well for my use case and should be easy to maintain. Posting it here for reference to anyone who wants to try it out
def deep_apply object, klasses, &blk
if object.is_a? Array
object.map { |obj_ele| deep_apply(obj_ele, klasses, &blk) }
elsif object.is_a? Hash
object.update(object) {|_, value| deep_apply(value, klasses, &blk) }
elsif klasses.any? { |klass| object.is_a? klass }
blk.call(object)
else
object
end
end
usage:
=> pry(main)> deep_apply({a: [1, 2, "sadsad"]}, [String, Integer]) { |v| v.to_s + "asd" }
=> {:a=>["1asd", "2asd", "sadsadasd"]}
Interesting to learn of the deep_merge approach taken in the answer by "The F". Here is another approach which requires adding a few helper methods.
First, the helper methods:
From the top answer here (converting-a-nested-hash-into-a-flat-hash):
def flat_hash(h,f=[],g={})
return g.update({ f=>h }) unless h.is_a? Hash
h.each { |k,r| flat_hash(r,f+[k],g) }
g
end
From a Github repo called ruby-bury (this functionality was proposed to Ruby core, but rejected)
class Hash
def bury *args
if args.count < 2
raise ArgumentError.new("2 or more arguments required")
elsif args.count == 2
self[args[0]] = args[1]
else
arg = args.shift
self[arg] = {} unless self[arg]
self[arg].bury(*args) unless args.empty?
end
self
end
end
And then a method tying it together:
def change_all_values(hash, &blk)
# the next line makes the method "pure functional"
# but can be removed otherwise.
hash = Marshal.load(Marshal.dump(hash))
flat_hash(hash).each { |k,v| hash.bury(*(k + [blk.call(v)])) }
hash
end
A usage example:
irb(main):063:0> a = {a: 1, b: { c: 1 } }
=> {:a=>1, :b=>{:c=>1}}
irb(main):064:0> b = change_all_values(a) { |val| val + 1 }
=> {:a=>2, :b=>{:c=>2}}
irb(main):066:0> a
=> {:a=>1, :b=>{:c=>1}}
There is deep_merge
yourhash.deep_merge(yourhash) {|_,_,v| v.to_s}
Merge the hash with itself, inspect the value and call to_s on it.
This method requires require 'active_support/core_ext/hash' at the top of file if you are not using ruby on rails.
Obviously, you may handle the conversion of v inside the deep_merge as you like to meet your requirements.
In rails console:
2.3.0 :001 > h1 = { a: true, b: { c: [1, 2, 3] } }
=> {:a=>true, :b=>{:c=>[1, 2, 3]}}
2.3.0 :002 > h1.deep_merge(h1) { |_,_,v| v.to_s}
=> {:a=>"true", :b=>{:c=>"[1, 2, 3]"}}
Well, it's quite simple to write it - so why don't write your own and be absolutely sure how does it behave in all situations ;)
def to_utf8(h)
if h.is_a? String
return h.force_encoding('utf-8')
elsif h.is_a? Symbol
return h.to_s.force_encoding('utf-8').to_sym
elsif h.is_a? Numeric
return h
elsif h.is_a? Array
return h.map { |e| to_utf8(e) }.to_s
else
return h.to_s.force_encoding('utf-8')
end
return hash.to_a.map { |e| result.push(to_utf8(e[0], e[1])) }.to_h
end
You may want to check if all behavior and conversions are correct - and change it if necessary.

How to refactor each function with map in Ruby?

I have a loop building a hash for use in a select field. The intention is to end up with a hash:
{ object.id => "object name", object.id => "object name" }
Using:
#hash = {}
loop_over.each do |ac|
#hash[ac.name] = ac.id
end
I think that the map method is meant for this type of situation but just need some help understanding it and how it works. Is map the right method to refactor this each loop?
Data transformations like this are better suited to each_with_object:
#hash = loop_over.each_with_object({}) { |ac, h| h[ac.name] = ac.id }
If your brain is telling you to use map but you don't want an array as the result, then you usually want to use each_with_object. If you want to feed the block's return value back into itself, then you want inject but in cases like this, inject requires a funny looking and artificial ;h in the block:
#hash = loop_over.inject({}) { |h, ac| h[ac.name] = ac.id; h }
# -------------------- yuck -----------------------------^^^
The presence of the artificial return value is the signal that you want to use each_with_object instead.
Try:
Hash[loop_over.map { |ac| [ac[:name], ac[:id]] }]
Or if you are running on Ruby 2:
loop_over.map { |ac| [ac[:name], ac[:id]] }.to_h
#hash = Hash[loop_over.map { |ac| {ac.name => ac.id} }.map(&:flatten)]
Edit, a simpler solution as per suggestion in a comment.
#hash = Hash[ loop_over.map { |ac| [ac.name, ac.id] } ]
You can simply do this by injecting a blank new Hash and performing your operation:
loop_over.inject({}){ |h, ac| h[ac.name] = ac.id; h }
Ruby FTW
No a map isn't the correct tool for this.
The general use-case of a map is to take in an array, perform an operation on each element, and spit out a (possibly) new array (not a hashmap) of the same length, with the individual element modifications.
Here's an example of a map
x = [1, 2, 3, 4].map do |i|
i+1 #transform each element by adding 1
end
p x # will print out [2, 3, 4, 5]
Your code:
#hash = {}
loop_over.each do |ac|
#hash[ac.name] = ac.id
end
There is nothing wrong with this example. You are iterating over a list, and populating a hashmap exactly as you wished.
Ruby 2.1.0 introduces brand new method to generate hashes:
h = { a: 1, b: 2, c: 3 }
h.map { |k, v| [k, v+1] }.to_h # => {:a=>2, :b=>3, :c=>4}
I would go for the inject version, but use update in the block to avoid the easy to miss (and therefore error prone) ;h suffix:
#hash = loop_over.inject({}) { |h, ac| h.update(ac.name: ac.id) }

What is ->() { } in Ruby?

I've just seen this expression in a Ruby/Rails app:
def method(a, b = nil, &c)
c ||= ->(v) { v }
I understand the first part, but not the ->() { ... } syntax. What does it mean?
The variable names have been changed for briefness. I tried searching, but the non-alphanumeric characters are obviously a nightmare for SEO.
It is a lambda literal. Put the block variables inside () and the body inside {}.
->(x, y){x + y}
In the example, ->(v){v} takes a single argument v and returns it, in other words, it is an identity function. If a block is passed to method, then that is assigned to c. If not, the identity function is assigned to c as default.
That is a lambda literal, introduced in Ruby 1.9:
irb> l = ->(v) { v }
# => #<Proc:0x007f4acea30410#(irb):1 (lambda)>
irb> l.call(1)
# => 1
It is equivalent to write:
irb> l = lambda { |v| v }
# => #<Proc:0x00000001daf538#(irb):1 (lambda)>
In the example you posted it is used to provide a default block to the method when none is specified, consider this:
def method(a, &c)
c ||= ->(v) { v }
c.call(a)
end
method(1)
# => 1
method(1) { |v| v * 2 }
# => 2

Reimplementing Enumerable Map method in Ruby

I'm practicing for an internship interview at a ruby shop. One of the job questions I'm expecting is to reimplement an enumerable method.
I'm trying to implement map right now and I'm having trouble figuring out how to implement the case where a block is not given.
class Array
def mapp()
out = []
if block_given?
self.each { |e| out << yield(e) }
else
<-- what goes here? -->
end
out
end
end
Using my current implementation. If I run:
[1,2,3,4,5,6].mapp{|each| each+1} #returns => [2,3,4,5,6,7]
However, I'm not sure how to get cases where a block isn't passed in:
[1,2,3,4].mapp("cat") # should return => ["cat", "cat", "cat", "cat"]
If someone could point me in the right direction. I'd really appreciate it. I tried looking through the source code but it seems to do things very differently than what i'm used to.
static VALUE
enum_flat_map(VALUE obj)
{
VALUE ary;
RETURN_SIZED_ENUMERATOR(obj, 0, 0, enum_size);
ary = rb_ary_new();
rb_block_call(obj, id_each, 0, 0, flat_map_i, ary);
return ary;
}
I suppose that by [1,2,3,4].mapp("cat") you mean [1,2,3,4].mapp{"cat"}.
That said, map without a block returns an enumerator:
[1,2,3,4].map
=> #<Enumerator: [1, 2, 3, 4]:map>
That is the same output of to_enum
[1,2,3,4].to_enum
=> #<Enumerator: [1, 2, 3, 4]:each>
So in your code, you just want to call to_enum:
class Array
def mapp()
out = []
if block_given?
self.each { |e| out << yield(e) }
else
out = to_enum :mapp
end
out
end
end
return to_enum :mapp unless block_given?
Should be sufficient.
See the Implementation of map of the Rubinius implementation which is entirely in Ruby:
https://github.com/rubinius/rubinius/blob/master/kernel/bootstrap/array19.rb
# -*- encoding: us-ascii -*-
class Array
# Creates a new Array from the return values of passing
# each element in self to the supplied block.
def map
return to_enum :map unless block_given?
out = Array.new size
i = #start
total = i + #total
tuple = #tuple
out_tuple = out.tuple
j = 0
while i < total
out_tuple[j] = yield tuple.at(i)
i += 1
j += 1
end
out
end
end
With rubinius there is a ruby implementation written in ruby wherever possible. You can look at their code for enumerable.#collect
Interesting is the difference between
their ruby 1.9 implementation and
their ruby 1.8 implementation.
class Array
def map!
return to_enum :map! unless block_given?
self.each_with_index { |e, index| self[index] = yield(e) }
end
end
Check out the documentation for Object#to_enum

returning a method reference wrapped in a lambda

Please help me understand WHY the following works.
class Dog
def bark
"woof"
end
end
bark_string = Dog.new.bark
puts bark_string # "woof" - a string at this point
ref_to_bark = -> { bark_string } # the string a moment ago is now the method again
ref_to_bark.call # "woof"
Why does wrapping a reference to a method in a proc/lambda return a reference to the original method? It baffles me.
It doesn't. ref_to_bark just returns bark_string, the bark method is not called.
Lambdas (and blocks, and procs) in Ruby are closures; this means that local variables available in the same scope as the lambda is defined are accessible inside the lambda. For example:
foo = 42
l = lambda{ p foo }
l.call()
#=> 42
The above should not be any more surprising than the fact that this code works:
x = 17
[1,2,3].map do |n|
n+x # Whoa, you can use _x_ here?!
end
#=> [18,19,20]
It's slightly more surprising when you do something like this:
def make_adder( x )
->(y){ x+y }
end
add10 = make_adder(10)
z = add10.call(32) #=> 42
Again, the local variable x (the parameter passed to the method) is "closed over" by the lambda, its value preserved for reference whenever the lambda is invoked.
So in your example the lambda is just "capturing" the bark_string variable and returning its value later on. Your method is never invoked a second time.
Note that a closure captures the variable itself, not just the object referred to by the variable:
x = "hello"
y = x # reference the same _object_
l = ->{ x } # close over the _variable_ itself
x = "world" # change the variable to point to a new object
p y, #=> "hello" (same object as the original)
l[] #=> "world" (new object)
A lambda defined using -> is called lambda literal. Sometimes called stabby lambda. The following will also return the same result.
ref_to_bark = lambda { bark_string }
Or
ref_to_bark = lambda { "woof" }

Resources