I am just starting to do Groovy after mostly doing ruby.
It has a default 'block argument', it, as it were, not officially the terminology for Groovy, but I'm new to Groovy.
(1..10).each {println(it)}
What about Ruby? Is there a default I can use so I don't have to make |my_block_arg| every time?
Thanks!
No, you don't have a "default" in Ruby.
Though, you can do
(1..10).each(&method(:puts))
Like Andrey Deinekos answer explained there is no default. You can set the self context using BasicObject#instance_eval or BasicObject#instance_exec. I don't recommend doing this since it can sometimes result in some unexpected results. However if you know what you're doing the following is still an option:
class Enumerator
def with_ie(&block)
return to_enum(__method__) { each.size } unless block_given?
each { |e| e.instance_eval(&block) }
end
end
(1..10).each.with_ie { puts self }
# 1
# 2
# 3
# 4
# 5
# 6
# 7
# 8
# 9
# 10
#=> 1..10
(1..10).map.with_ie { self * self }
#=> [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
(-5..5).select.with_ie { positive? }
#=> [1, 2, 3, 4, 5]
If you want to call one method you might as well do (-5..5).select(&:positive?), but when the objects you're iterating over have actual attributes it might be worth the trouble. For example:
people.map.with_ie { "#{id}: #{first_name} - #{last_name}" }
Keep in mind that if you have an local variable id, first_name or last_name in scope those are used instead of the methods on the object. This also doesn't quite work for hashes or Enumerable methods that pass more than one block argument. In this case self is set to an array containing the arguments. For example:
{a: 1, b: 2}.map.with_ie { self }
#=> [[:a, 1], [:b, 2]]
{a: 1, b: 2}.map.with_ie { self[0] }
#=> [:a, :b]
From Ruby 2.7 onwards, you can use numbered block arguments:
(1..10).each { puts _1 }
Granted, this hasn't been very well documented; some references are still using #1, but the above is tested on the official 2.7 version.
Related
I've searched extensively but sadly couldn't find a solution to this surely often-asked question.
In Perl I can reassign an entire array within a function and have my changes reflected outside the function:
#!/usr/bin/perl -w
use v5.20;
use Data::Dumper;
sub foo {
my ($ref) = #_;
#$ref = (3, 4, 5);
}
my $ref = [1, 2];
foo($ref);
say Dumper $ref; # prints [3, 4, 5]
Now I'm trying to learn Ruby and have written a function where I'd like to change an array items in-place by filtering out elements matching a condition and returning the removed items:
def filterItems(items)
removed, items = items.partition { ... }
After running the function, items returns to its state before calling the function. How should I approach this please?
I'd like to change an array items in-place by filtering out elements matching a condition and returning the removed items [...] How should I approach this please?
You could replace the array content within your method:
def filter_items(items)
removed, kept = items.partition { |i| i.odd? }
items.replace(kept)
removed
end
ary = [1, 2, 3, 4, 5]
filter_items(ary)
#=> [1, 3, 5]
ary
#=> [2, 4]
I would search for pass by value/reference in ruby. Here is one I found first https://mixandgo.com/learn/is-ruby-pass-by-reference-or-pass-by-value.
You pass reference value of items to the function, not the reference to items. Variable items is defined out of method scope and always refers to same value, unless you reassign it in the variable scope.
Also filterItems is not ruby style, see https://rubystyle.guide/
TL;DR
To access or modify an outer variable within a block, declare the variable outside the block. To access a variable outside of a method, store it in an instance or class variable. There's a lot more to it than that, but this covers the use case in your original post.
Explanation and Examples
In Ruby, you have scope gates and closures. In particular, methods and blocks represent scope gates, but there are certainly ways (both routine and meta) for accessing variables outside of your local scope.
In a class, this is usually handled by instance variables. So, as a simple example of String#parition (because it's easier to explain than Enumerable#partition on an Array):
def filter items, separator
head, sep, tail = items.partition separator
#items = tail
end
filter "foobarbaz", "bar"
#=> "baz"
#items
#=> "baz"
Inside a class or within irb, this will modify whatever's passed and then assign it to the instance variable outside the method.
Partitioning Arrays Instead of Strings
If you really don't want to pass things as arguments, or if #items should be an Array, then you can certainly do that too. However, Arrays behave differently, so I'm not sure what you really expect Array#partition (which is inherited from Enumerable) to yield. This works, using Enumerable#slice_after:
class Filter
def initialize
#items = []
end
def filter_array items, separator
#items = [3,4,5].slice_after { |i| i == separator }.to_a.pop
end
end
f = Filter.new
f.filter_array [3, 4, 5], 4
#=> [5]
Look into the Array class for any method which mutates the object, for example all the method with a bang or methods that insert elements.
Here is an Array#push:
ary = [1,2,3,4,5]
def foo(ary)
ary.push *[6, 7]
end
foo(ary)
ary
#=> [1, 2, 3, 4, 5, 6, 7]
Here is an Array#insert:
ary = [1,2,3,4,5]
def baz(ary)
ary.insert(2, 10, 20)
end
baz(ary)
ary
#=> [1, 2, 10, 20, 3, 4, 5]
Here is an example with a bang Array#reject!:
ary = [1,2,3,4,5]
def zoo(ary)
ary.reject!(&:even?)
end
zoo(ary)
ary
#=> [1, 3, 5]
Another with a bang Array#map!:
ary = [1,2,3,4,5]
def bar(ary)
ary.map! { |e| e**2 }
end
bar(ary)
ary
#=> [1, 4, 9, 16, 25]
I have an hash
{:result=>
{:"1"=>
[{:"1"=>1,
:"2"=>"4698192612070913717",
:"5"=>
{:"1"=>{:"1"=>"1.0.0"},
:"2"=>
{:"1"=>1,
:"2"=>"1525341956127",
:"3"=>1000000000,
:"4"=>0,
:"5"=>{:"1"=>1000000000}},
:"3"=>["17"],
:"6"=>"4704522736971289334",
:"8"=>["4618851880555471022"],
:"9"=>[1]},
:"6"=>{:"3"=>{:"2"=>false}, :"4"=>{:"1"=>false}},
:"7"=>{:"1"=>1},
:"8"=>"production"},
{:"1"=>4,
:"2"=>"4700283765268993541",
:"6"=>{:"2"=>{:"1"=>200}, :"3"=>{:"2"=>false}, :"4"=>{:"1"=>false}},
:"8"=>"beta"},
{:"1"=>5,
:"2"=>"4699074054925986704",
:"6"=>{:"2"=>{:"1"=>100}, :"3"=>{:"2"=>false}, :"4"=>{:"1"=>false}},
:"8"=>"alpha"},
{:"1"=>10,
:"2"=>"4697702456121346981",
:"6"=>{:"2"=>{:"1"=>50}, :"3"=>{:"2"=>false}, :"4"=>{:"1"=>false}},
:"8"=>"internal"}],
:"3"=>{:"1"=>true, :"2"=>{:"1"=>{:"1"=>false}, :"2"=>{:"1"=>false}}},
:"4"=>false},
:xsrf=>"AMtNNDFJl06mR54j2zxFjYIYfGQR22sUKA:1528830206790"}
I am looking for simple way to return a value or nil
I have tried this
result[:'result'][:'1'][1].dig(:'5').dig(:'1').dig(:'1')
but it's not working
What can I do to avoid this
if result[:'result'][:'1'][1].dig(:'5')
puts result[:'result'][:'1'][1][:'5'][:'1'][:'1']
end
The idea behind dig is that you can go several levels deep into a hash at the same time and return nil if the key doesn't exist at any level during the 'digging'. So result[:'result'][:'1'][1].dig(:'5', :'1', :'1') will do what you are looking for and clean up your code as well. In fact, you could make it a little safer if you wanted by doing result.dig(:result, :'1', 1, :'5', :'1', :'1')
dig is not a single method, but a family of four methods, all of which made their debut in Ruby v2.3: Array#dig, Hash#dig, Struct#dig and OpenStruct#dig.
For example,
h = { a: [1, { c: 2, d: 3 }], b: 2 }
h.dig(:a, 1, :d)
#=> 3
employs Hash#dig because dig's receiver is a hash. Moreover, one might expect that when, in an intermediate calculation, dig has unearthed [1, { c: 2, d: 3 }] it will pass the shovel to Array#dig for further excavation.
Suppose
h = { a: [1, 2] }
Then
h.dig(:a, 1) #=> 2
h.dig(:a).dig(1) #=> 2
Does that mean the two are equivalent? Try this:
h.dig('cat', 1) #=> nil
h.dig('cat').dig(1) #=> NoMethodError: undefined method `dig' for nil:NilClass
The exception is due to the fact that h.dig('cat') #=> nil and NilClass has no instance method dig, so nil.dig(1) raises the exception. No, the two expressions are not equivalent.
If the value of the variable result is the OPs hash, we have (as pointed out by #Isaiah) the following.
result.dig(:result, :'1', 0, :"5", :"1", :"1")
#=> "1.0.0"
result.dig(:result, :'1', 0, :cat, :"1", :"1")
#=> nil
Note that dig will still raise an exception if the wrong data type is used:
[1, 2].dig(:a)
#=> TypeError: no implicit conversion of Symbol into Integer
To support versions of Ruby prior to 2.3 (where dig is not available) we can write the following, using Enumerable#reduce (aka inject).
arr = [:result, :'1', 0, :"5", :"1", :"1"]
arr.reduce(result) { |memo, obj| memo && memo[obj] }
#=> "1.0.0"
arr = [:result, :'1', 0, :cat, :"1", :"1"]
arr.reduce(result) { |memo, obj| memo && memo[obj] }
#=> nil
This question already has answers here:
Can you supply arguments to the map(&:method) syntax in Ruby?
(9 answers)
Closed 8 years ago.
Given the following array a:
a = [1, 2, 3, 4, 5]
How do I do:
a.map { |num| num + 1 }
using the short notation:
a.map(&:+ 1)
or:
a.map(&:+ 2)
where 1 and 2 are the arguments?
In this case you can do
a.map(&1.method(:+))
But only because 1 + x is usually the same as x + 1.
Here is a discussion of this practice in a performance context.
You can't do it like this. The & operator is for turning symbols into procs.
a = [1, 2, 3, 4, 5]
puts a.map(&:to_s) # prints array of strings
puts a.map(&:to_s2) # error, no such method `to_s2`.
& is a shorthand for to_proc:
def to_proc
proc { |obj, *args| obj.send(self, *args) }
end
It creates and returns new proc. As you see, you can't pass any parameters to this method. You can only call the generated proc.
You cannot do it with map. But look at Facets' Enumerable#map_send:
require 'facets'
[1, 2, 3].map_send(:+, 1)
#=> [2, 3, 4]
Writing your own implementation is pretty straightforward:
module Enumerable
def map_send(*args)
map { |obj| obj.send(*args) }
end
end
If you really need that you can use Ampex library, but I don't know if it is still maintained.
Given this irb session:
[2.0.0p195]> arr = [{count: 5}, {count: 6}, {count: 7}]
=> [{:count=>5}, {:count=>6}, {:count=>7}]
[2.0.0p195]> arr.collect(&:count)
=> [1, 1, 1]
wat
[2.0.0p195]> arr.collect(&:count).reduce(:+)
=> 3
[2.0.0p195]> arr.collect {|e| e[:count]}.reduce(:+)
=> 18
Can I exclude methods on Hash when collecting or is using a block the only way around this problem?
& means call #to_proc on its argument, and the Symbol class implements this by creating a Proc that calls the method name based on the symbol - so &:symbol means "Call the #symbol method on the passed in object". Essentially, what you've got is the equivalent of this:
arr.collect{|obj| obj.send(:count)}
Since Hash won't respond to the "count" method at all to get the value of the :count key - that is, Hash#count is not the same as Hash#[](:count), (though OpenStruct does do this for you), you're stuck with the block method.
Another alternative is to create a lambda, useful if you are writing the same block many times:
fetch_count = -> x{x[:count]}
arr.collect(&fetch_count) #=> [5, 6, 7]
# If hash only has one value as in example:
arr.collect(&values).flatten #=> [5, 6, 7]
The implementation of calling & on a symbol is as follows (more or less):
class Symbol
def to_proc
Proc.new { |obj| obj.send self }
end
end
You can see that all it is doing (when combined with a #map) is calling the method corresponding to the provided symbol on each member of the enumerable.
You could fix this if you really wanted by using OpenStructs instead of hashes, they have method-style access of elements:
[{test: 1}].map { |h| OpenStruct.new(h) }.map &:test
#=> [1]
Or invent an operator that does what you want for hash access in addition to &, I may revisit this challenge if I have a spare moment later!
EDIT: I have returned
This is hacky but you could monkey-patch symbol to provide the functionality that you wish for by augmenting with unary ~:
# Patch
class Symbol
def ~#
->(obj){ obj[self] }
end
end
# Example usage:
[{count: 5}, {count: 6}, {count: 7}].map &~:count
#=> [5, 6, 7]
If a free-for-all language such as Ruby doesn't have a feature that you wish for, you can always build it in :-)
Disclaimer: This is probably a terrible idea.
Following is the code I tried to run from the Ruby Programming Book
http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_modules.html
Why doesn't the product method give the right output?
I ran it with irb test.rb. And I am running Ruby 1.9.3p194.
module Inject
def inject(n)
each do |value|
n = yield(n, value)
end
n
end
def sum(initial = 0)
inject(initial) { |n, value| n + value }
end
def product(initial = 1)
inject(initial) { |n, value| n * value }
end
end
class Array
include Inject
end
[1, 2, 3, 4, 5].sum ## 15
[1, 2, 3, 4, 5].product ## [[1], [2], [3], [4], [5]]
Since that code example was written, Array has gained a #product method and you're seeing the output of that particular method. Rename your module's method to something like product_new.
Add this line at the end of your code :
p Array.ancestors
and you get (in Ruby 1.9.3) :
[Array, Inject, Enumerable, Object, Kernel, BasicObject]
Array is a subclass of Object and has a superclass pointer to Object. As Enumerable is mixed in (included) by Array, the superclass pointer of Array points to Enumerable, and from there to Object. When you include Inject, the superclass pointer of Array points to Inject, and from there to Enumerable. When you write
[1, 2, 3, 4, 5].product
the method search mechanism starts at the instance object [1, 2, 3, 4, 5], goes to its class Array, and finds product (new in 1.9) there. If you run the same code in Ruby 1.8, the method search mechanism starts at the instance object [1, 2, 3, 4, 5], goes to its class Array, does not find product, goes up the superclass chain, and finds product in Inject, and you get the result 120 as expected.
You find a good explanation of Modules and Mixins with graphic pictures in the Pickaxe http://pragprog.com/book/ruby3/programming-ruby-1-9
I knew I had seen that some are asking for a prepend method to include a module before, between the instance and its class, so that the search mechanism finds included methods before the ones of the class. I made a seach in SO with "[ruby]prepend module instead of include" and found among others this :
Why does including this module not override a dynamically-generated method?
By the way: in Ruby 2.0, there are two features which help you with both your problems.
Module#prepend prepends a mixin to the inheritance chain, so that methods defined in the mixin override methods defined in the module/class it is being mixed into.
Refinements allow lexically scoped monkeypatching.
Here they are in action (you can get a current build of YARV 2.0 via RVM or ruby-build easily):
module Sum
def sum(initial=0)
inject(initial, :+)
end
end
module ArrayWithSum
refine Array do
prepend Sum
end
end
class Foo
using ArrayWithSum
p [1, 2, 3].sum
# 6
end
p [1, 2, 3].sum
# NoMethodError: undefined method `sum' for [1, 2, 3]:Array
using ArrayWithSum
p [1, 2, 3].sum
# 6
In response to #zeronone "How can we avoid such namespace clashes?"
Avoid monkeypatching core classes wherever possible is the first rule. A better way to do this (IMO) would be to subclass Array:
class MyArray < Array
include Inject
# or you could just dispense with the module and define this directly.
end
xs = MyArray.new([1, 2, 3, 4, 5])
# => [1, 2, 3, 4, 5]
xs.sum
# => 15
xs.product
# => 120
[1, 2, 3, 4, 5].product
# => [[1], [2], [3], [4], [5]]
Ruby may be an OO language, but because it is so dynamic sometimes (I find) subclassing gets forgotten as a useful way to do things, and hence there is an over reliance on the basic data structures of Array, Hash and String, which then leads to far too much re-opening of these classes.
The following code is not very elaborated. Just to show you that today you already have means, like the hooks called by Ruby when certain events occur, to check which method (from the including class or the included module) will be used/not used.
module Inject
def self.append_features(p_host) # don't use included, it's too late
puts "#{self} included into #{p_host}"
methods_of_this_module = self.instance_methods(false).sort
print "methods of #{self} : "; p methods_of_this_module
first_letter = []
methods_of_this_module.each do |m|
first_letter << m[0, 2]
end
print 'selection to reduce the display : '; p first_letter
methods_of_host_class = p_host.instance_methods(true).sort
subset = methods_of_host_class.select { |m| m if first_letter.include?(m[0, 2]) }
print "methods of #{p_host} we are interested in: "; p subset
methods_of_this_module.each do |m|
puts "#{self.name}##{m} will not be used" if methods_of_host_class.include? m
end
super # <-- don't forget it !
end
Rest as in your post. Execution :
$ ruby -v
ruby 1.8.6 (2010-09-02 patchlevel 420) [i686-darwin12.2.0]
$ ruby -w tinject.rb
Inject included into Array
methods of Inject : ["inject", "product", "sum"]
selection to reduce the display : ["in", "pr", "su"]
methods of Array we are interested in: ["include?", "index",
..., "inject", "insert", ..., "instance_variables", "private_methods", "protected_methods"]
Inject#inject will not be used
$ rvm use 1.9.2
...
$ ruby -v
ruby 1.9.2p320 (2012-04-20 revision 35421) [x86_64-darwin12.2.0]
$ ruby -w tinject.rb
Inject included into Array
methods of Inject : [:inject, :product, :sum]
selection to reduce the display : ["in", "pr", "su"]
methods of Array we are interested in: [:include?, :index, ..., :inject, :insert,
..., :private_methods, :product, :protected_methods]
Inject#inject will not be used
Inject#product will not be used