Chaining & to_proc on symbol - ruby

It's well known to Rubyist & will call to_proc on a symbol, so
[:a, :b, :c].map(&:to_s)
is equivalent to
[:a, :b, :c].map { |e| e.to_s } # => ["a", "b", "c"]
Say I want to call another method right after to_s, these two implementations will work:
[:a, :b, :c].map { |e| e.to_s.upcase }
[:a, :b, :c].map(&:to_s).map(&:upcase)
My question is, is there a way to chain the & Symbol#to_proc call in one parameter? Something like:
[:a, :b, :c].map(&:to_s:upcase)
Thanks!

If you're only doing:
%i[a b c].map { |e| e.to_s.upcase }
then just use the block and get on with more important things. If you're really doing a chain of Enumerable calls and find the blocks too visually noisy:
%i[a b c].map { |e| e.to_s.upcase }.some_chain_of_enumerable_calls...
then you could toss your logic into a lambda to help clean up the appearance:
to_s_upcase = lambda { |e| e.to_s.upcase }
%i[a b c].map(&to_s_upcase).some_chain_of_enumerable_calls...
or throw it in a method and say:
%i[a b c].map(&method(:to_s_upcase)).some_chain_of_enumerable_calls...
Either way, you're giving your little bit of logic a name (which is pretty much all &:symbol is doing for you) to make the code more readable and easier to understand. In the specific case of to_s.upcase, this is all a bit pointless but these approaches are quite useful when the block gets bigger.

You will need to define some method in advance, but this will have generality. You can do like this:
class Symbol
def * other
->x{x.send(self).send(other)}
end
end
[:a, :b, :c].map(&:to_s * :upcase)
[:a, :b, :c].map(&:to_s * :capitalize)
...
I chose * as a method for functional composition.
And if you think you might use a third symbol, you can define like:
class Proc
def * other
->x{call(x).send(other)}
end
end

So just for fun (and to prove that almost anything is possible in ruby if one puts in a bit of effort) we could define a method on Symbol (we'll call it Symbol#chain) to provide this functionality and a little more
class Symbol
def proc_chain(*args)
args.inject(self.to_proc) do |memo,meth|
meth, *passable_args = [meth].flatten
passable_block = passable_args.pop if passable_args.last.is_a?(Proc)
Proc.new do |obj|
memo.call(obj).__send__(meth,*passable_args,&passable_block)
end
end
end
alias_method :chain, :proc_chain
end
This can then be called like so
[:a, :b, :c].map(&:to_s.chain(:upcase))
#=> ["A","B","C"]
# Or with Arguments & blocks
[1,2,3,4,5].map(&:itself.chain([:to_s,2],:chars,[:map,->(e){ "#{e}!!!!"}]))
#=> => [["1!!!!"], ["1!!!!", "0!!!!"], ["1!!!!", "1!!!!"],
# ["1!!!!","0!!!!", "0!!!!"], ["1!!!!", "0!!!!", "1!!!!"]]
Can even be used as a standalone
p = :to_s.chain([:split,'.'])
p.call(123.45)
#=> ["123","45"]
# Or even
[123.45,76.75].map(&p)
#=> => [["123", "45"], ["76", "75"]]

While we're playing with syntax, how about monkey-patching Array with a to_proc method?
class Array
def to_proc
return :itself.to_proc if empty?
->(obj) { drop(1).to_proc.call(first.to_proc.call(obj)) }
end
end
["foo", "bar", "baz"].map(&[:capitalize, :swapcase, :chars, ->a{ a.join("-") }])
# => ["f-O-O", "b-A-R", "b-A-Z"]
See it on repl.it: https://repl.it/JS4B/1

There is no way to chain using the symbol to proc.
However, you could monkey patch a method to the class you are mapping over that will do both, then call that.
class Symbol
def to_upcase_str
self.to_s.upcase
end
end
[:a, :b, :c].map(&:to_upcase_str)

I am surprised no body mentioned about Proc#<< and Proc#>>:
[:a, :b, :c].map(&(:to_s.to_proc << :upcase.to_proc))
# => ["A", "B", "C"]
[:a, :b, :c].map(&(:upcase.to_proc >> :to_s.to_proc))
# => ["A", "B", "C"]
ref: https://ruby-doc.org/core-2.7.2/Proc.html#method-i-3C-3C

Related

Getting #with_index when inheriting from Array

I'd like my SomeArray#map to return an "array" of the SomeArray class.
class SomeArray < Array
def map
SomeArray.new(super)
end
end
some_array = SomeArray.new(["foo", "bar", "baz"])
p some_array.class #=> SomeArray
p some_array.map { |e| e }.class #=> SomeArray
Except now I also want to be able to use the Enumerator#with_index instance method. So ideally something like this would work:
some_array.map.with_index { |e, i| e }.class #=> SomeArray
How would that work?
I've tried:
class SomeArray < Array
def map
SomeArray.new(super)
end
def with_index(offset = 0)
super
end
end
some_array = SomeArray.new(["foo", "bar", "baz"])
p some_array.class #=> SomeArray
p some_array.map.with_index { |e, i| e }.class #=> no implicit conversion of Enumerator into Integer (TypeError)
But it's not working.
I think the problem here is that you're treating enumerable and array as if they're the same, which they're not.
Specifically, this in the map call: SomeArray.new(super).
I can reproduce your error:
[6] pry(main)> Array.new [1].map
TypeError: no implicit conversion of Enumerator into Integer
Now, when you pass a block to map, it works:
Array.new([1].map { |x| x })
=> [1]
But in your map.with_index you're not doing this.
You can do something like this:
module Foo
include Enumerable
def map
puts "calling my map"
super
end
def with_index
puts "calling my with_index"
super
end
end
class MyArr < Array
include Foo
end
puts MyArr.new([1]).map.with_index { |x, y| [x,y] }
# calling my map
# calling my with_index
# 1
# 0
Which kind of begs the question of why you're writing this class that just calls super. But in the case that you want to modify the default functionality of enumerable, this is one way.
The main problem is that map is an Array method while with_index is an Enumerator method.
Array methods are defined to just call super, and convert the output array to SomeArray.
Enumerator methods are defined with a to_enum first, convert the output to an Array and then to SomeArray.
It probably isn't the best structure for what you want to do, and isn't very efficient either. It's just a proof a concept!
class SomeArray < Array
# Array methods are overwritten here :
[:map, :select, :reject].each do |array_method_name|
define_method array_method_name do |*p, &block|
SomeArray.new(super(*p, &block).to_a)
end
end
# Enumerator methods are defined for SomeArray here :
[:with_index, :with_object].each do |enum_method_name|
define_method enum_method_name do |*p, &block|
SomeArray.new(to_enum.public_send(enum_method_name, *p, &block).to_a)
end
end
end
some_array = SomeArray.new(%w(foo bar baz biz))
p some_array.map { |s| s * 2 }.with_index.select { |_, i| i.even? }
#=> [["foofoo", 0], ["bazbaz", 2]]
p some_array.map { |s| s * 2 }.with_index.select { |_, i| i.even? }.class
#=> SomeArray

Module vs methods outside module

I would like to know what the difference is between using a module with methods inside it and using a simple file with many methods without creating a module, like the example below:
def doStuff
puts 'test';
end
module Example
def self.doStuff
puts 'test';
end
end
I could require both files and use the methods inside one. I want to know why I should create a module instead of having just a bunch of methods inside a .rb file.
Let's find out.
module M
def a; end
end
File.write("temp.rb", "def b\nputs 'hi'\nend") #=> 19
require_relative 'temp.rb'
class C
include M
end
C.instance_methods & [:a, :b] #=> [:a]
C.private_instance_methods & [:a, :b] #=> [:b]
C.instance_method(:b).owner #=> Object
Object.instance_methods.include? :b #=> false
Object.private_instance_methods.include? :b #=> true
Object.send(:public, :b) #=> Object
Object.instance_methods.include? :b #=> true
Object.private_instance_methods.include? :b #=> false
C.instance_methods & [:a, :b] #=> [:a, :b]
C.private_instance_methods & [:a, :b] #=> []
C.new.b #=> hi
The results are the same if we write:
class C
include M
require_relative 'temp.rb'
end
Modules allow you group your methods and you can include them in other modules and classes. You can include module methods into the module/class itself or its eigenclass. So, with modules you have more flexibility. Not to mention that you are not cluttering up the "global" namespace.
Also, when you define method in a file, you are defining it in the current context. Since, everything in Ruby is an object, you are actually defining it within a main object (an instance of Object class).
2.3.0 :001 > self
=> main
2.3.0 :002 > self.class
=> Object
2.3.0 :003 > self.object_id
=> 70123533022000
2.3.0 :004 > def test; end;
2.3.0 :005 > self.public_methods.include? :test
=> true

Ways to refactor named arguments with default values

I have a method with a lot of named arguments, some with default values:
def myClass
def initialize(a:, b:, c:, d:, e:, f:, g: nil, h: nil, i: nil)
...
end
end
The list is a little hard to look at and comprehend. I am looking for way to make this simpler.
Using a hash for args,
myClass.new(**args)
works, but I can't have both symbols with and without a value.
Is there a way to make this simpler?
You could try this
def myClass
def initialize(args)
[:a, :b, :c, :d, :e, :f].each do |a|
raise ArgumentError.new unless args.has_key?(a)
end
...
end
end
args is a hash object.
May be there are cases where a function needs such a large number of parameters, but normally this indicates that a function is doing too many things in one place.
Ok, if you want to do it, I would move it into a special private method:
class MyClass
def initialize(*args)
args = set_defaults(args)
end
private
def set_defaults(args)
# step 1: extract the options hash and check the keys,
# if a key doesn't exist so put it in with the default value
options = args.extract_options!
[g: :state, h: 'a name', i: 5].each do |key, value|
options[key] = value unless options.key?(key)
end
# step 2: check the other array elements
[:a, :b, :c, :d, :e, :f].each do |element|
raise ArgumentError.new unless args.include?(element)
end
# step 3: put them all together again
args << options
end
end
BTW: def className doesn't work. It's class ClassName. In addition please have a look at the beautiful ruby style guide - naming.

Appending dynamic value to ruby instance variable

I know this code doesn't look good at all , but i just want to explain my requirement. I want to know is there any good or alternative approach to it.
Actually, i want to create a new stack and whenever one stack has reached its capacity. I want to keep track of number of stacks created like #stack_1, #stack_2 ...by incrementing #number += 1 like #stack_#number. And for every stack, i want to maintain a #current_position pointer which is specific to every stack like #stack_2 has #current_position_2. So i want to create dynamic instance variables.
Example:
def initialize
#number = 1
#stack+"#{#number}" = Array.new(10)
#current_position_"#{#number}" = 0
end
Output should be something like #stack1 = Array.new(10).
Lets say if i increment value of #number += 1, it should look like #stack2 = Array.new(10)
Instead of array I suggest you to use Hash Map
#stack = Hash.new
#stack[#number] = <Your Array>
Be Careful if the #number is same your array will be replaced..
For more information about hash maps http://www.ruby-doc.org/core-1.9.3/Hash.html
You can do it like this:
instance_variable_set("#stack#{#number}", Array.new(10, :a))
#stack1
#=> [:a, :a, :a, :a, :a, :a, :a, :a, :a, :a]
instance_variable_set("#stack#{#number+1}", Array.new(10, :b))
#stack2
#=> [:b, :b, :b, :b, :b, :b, :b, :b, :b, :b]
instance_variable_set("#current_position_#{#number}", 0)
#current_position_1
#=> 0
Instead of creating instance variables to track a stack's state from the outside, you could create a Stack class that tracks its state internally. Here's a very simple one:
class StackOverflow < StandardError; end
class Stack
def initialize
#stack = []
end
def position
#stack.size
end
def full?
position == 2 # small size for demonstration purposes
end
def push(obj)
raise StackOverflow if full?
#stack << obj
end
end
stack = Stack.new
stack.push "foo"
stack.full? #=> false
stack.push "bar"
stack.full? #=> true
stack.push "baz" #=> StackOverflow
Having a working stack, you can build something like a StackGroup to handle multiple stacks:
class StackGroup
attr_reader :stacks
def initialize
#stacks = [Stack.new]
end
def push(obj)
#stacks << Stack.new if #stacks.last.full?
stacks.last.push(obj)
end
end
stack_group = StackGroup.new
stack_group.push "foo"
stack_group.push "bar"
stack_group.stacks.size #=> 1
stack_group.push "baz" # no error here
stack_group.stacks.size #=> 2
stack_group.stacks
#=> [#<Stack:0x007f9d8b886b18 #stack=["foo", "bar"]>,
# #<Stack:0x007f9d8b886a50 #stack=["baz"]>]

I have a complicated ruby object, how can I display all its methods and variables?

I have a complicated ruby object, how can I display all its methods and variables?
Is there some kind of meta call to pretty print all its values, methods and variables?
Doing as below :
object.class.instance_methods # => [:meth1, :meth2,...]
object.instance_variables # => [:#var1, :#var2,..]
Now below will give you values of all variables of an object.
object.instance_variables.map { |var| object.instance_variable_get(var) }
Docs are :-
instance_variables
instance_variable_get
instance_methods
To elaborate on what #Arup said:
To obtain all constants defined for a class or module, use MyClass.constants.
To get all class variables (e.g., ##a), use my_class.class_variables.
To get all class instance_variables (e.g., #a), use my_class.instance_variables.
To get all class methods, use my_class.singleton_class.instance_methods.
Sometimes you want just the constants, instance methods, class methods, class variables, class instance variables or instance variables that you have defined for a particular class (or, for methods, a particular module). To retrieve only those (that is, to exclude ones defined by a classes' or module's ancestors), use my_class.instance_methods(false), my_object.instance_variables(false), etc.
Example
module M
M_constant = "M constant"
def m_method
puts "M#m_instance_method"
end
end
class C
include M
attr_accessor :c_instance_variable
C_constant = "C constant"
##c_class_variable = 4
def self.c_class_method1
puts "c_class_method1"
end
def c_method
#c_instance_variable = 0
puts "c_instance_method"
end
class << self
attr_accessor :c_class_instance_variable
#c_class_instance_variable = 5
def c_class_method1
puts "c_class_method1"
end
end
end
class D < C
end
Objects
M.constants
#=> [:M_constant]
C.constants
#=> [:A, :C_constant, :M_constant]
C.constants(false)
#=> [:A, :C_constant]
D.constants
#=> [:A, :C_constant, :M_constant]
D.constants(false)
#=> []
C.class_variables
#=> [:##c_class_variable]
C.class_variables(false)
#=> [:##c_class_variable]
D.class_variables
#=> [:##c_class_variable]
D.class_variables(false)
#=> []
C.new.instance_variables
#=> [:#c_instance_variable]
D.new.instance_variables
#=> []
M.instance_methods
#=> [:m_instance_method]
C.instance_methods.size
#=> 57
C.instance_methods(false)
#=> [:c_instance_variable, :c_instance_variable=, :c_method]
D.instance_methods.size
#=> 57
D.instance_methods(false)
#=> []
C.singleton_class.instance_methods.size
#=> 102
C.singleton_class.instance_methods(false)
#=> [:c_class_method1, :c_class_instance_variable,
# :c_class_instance_variable=]
D.singleton_class.instance_methods.size
#=> 102
D.singleton_class.instance_methods(false)
#=> []

Resources