Call a block method on an iterator: each.magic.collect { ... } - ruby

I have a class with a custom each-method:
class CurseArray < Array
def each_safe
each do |element|
unless element =~ /bad/
yield element
end
end
end
end
And want to call different block methods, like "collect" or "inject" on those iterated elements. For example:
curse_array.each_safe.magic.collect {|element| "#{element} is a nice sentence."}
I know there is a specific function (which I called "magic" here) to do this, but I've forgotten. Please help! :-)

If a method yields you will need to pass it a block. There is no way define a block that automatically passes itself.
Closest I can get to your spec is this:
def magic(meth)
to_enum(meth)
end
def test
yield 1
yield 2
end
magic(:test).to_a
# returns: [1,2]
The cleanest way of implementing your request is probably:
class MyArray < Array
def each_safe
return to_enum :each_safe unless block_given?
each{|item| yield item unless item =~ /bad/}
end
end
a = MyArray.new
a << "good"; a << "bad"
a.each_safe.to_a
# returns ["good"]

The way you wrote your each_safe method, the easiest would be
curse_array.each_safe { |element| do_something_with(element) }
Edit: Oh, your each_safe method isn't correct, either. It has to be "each do", not "each.do"
Edit 2: If you really want to be able to do things like "each_safe.map", while at the same time also being able to do "each_safe { ... }" you could write your method like this:
require 'enumerator'
class CurseArray < Array
BLACKLIST = /bad/
def each_safe
arr = []
each do |element|
unless element =~ BLACKLIST
if block_given?
yield element
else
arr << element
end
end
end
unless block_given?
return Enumerator.new(arr)
end
end
end

The selected solution uses the common idiom to_enum :method_name unless block_given? which it's ok, but there are alternatives:
Leave your "unfriendly" yielder method untouched, use enum_for when calling it.
Use a lazy Enumerator.
Use lazy arrays (needs Ruby 2.0 or gem enumerable-lazy).
Here's a demo code:
class CurseArray < Array
def each_safe
each do |element|
unless element =~ /bad/
yield element
end
end
end
def each_safe2
Enumerator.new do |enum|
each do |element|
unless element =~ /bad/
enum.yield element
end
end
end
end
def each_safe3
lazy.map do |element|
unless element =~ /bad/
element
end
end.reject(&:nil?)
end
end
xs = CurseArray.new(["good1", "bad1", "good2"])
xs.enum_for(:each_safe).select { |x| x.length > 1 }
xs.each_safe2.select { |x| x.length > 1 }
xs.each_safe3.select { |x| x.length > 1 }.to_a

Related

Implementing &:method with custom methods

I've already read this question but I'm having trouble implementing the concepts there.
I'm doing an exercise from exercism.io that has provided tests. The aim of the exercise is to implement an accumulate method that returns the squares of the numbers passed to it. We need to do this without using map/inject.
That was no problem but one of the tests is as follows:
def test_accumulate_upcases
result = %w(hello world).accumulate(&:upcase)
assert_equal %w(HELLO WORLD), result
end
I have the following class
class Array
def accumulate
squares = []
self.each { |x| squares << x**2 unless x.is_a? String }
squares
end
def upcase
upcase = []
self.each { |word| word.upcase }
upcase
end
end
But I don't fully understand the concept being tested. How to I get accumulate to call methods that are passed to it as arguments?
It seems you are expected to extend Array class with new method accumulate which will accumulate the results of invoking a given proc on each element of the array.
One implementation can be like this:
class Array
def accumulate(&block)
self.collect { |i| block.yield(i) }
end
end
p result = %w(hello world).accumulate(&:upcase) # Prints ["HELLO", "WORLD"]
p result = %w(hello world).accumulate { |b| b.upcase } # Prints ["HELLO", "WORLD"]
Please note that %w(HELLO WORLD) is same as array ["HELLO", "WORLD"]
There is a very good explanation of what is the use of & operator in this article - please read the section on The Unary &
accumulate method is expecting to receive a name of the method as Symbol.
def accumulate meth = nil
result = []
case
when meth.nil?
self.each { |e| result << e.public_send meth }
when block_given?
self.each { |e| result << yield e }
else
fail 'This method expects either block or method name'
end
end

iterating over values in a block

I use blocks to create values like so
some_block = BlockClass.new {|b|
b.one = 1
b.two = 2
b.three = 3
}
Here is BlockClass
class BlockClass
attr_accessor :one
attr_accessor :two
attr_accessor :three
def initialize
yield self if block_given?
end
end
I need a way to iterate over some_block, and print all the value in the block without having to do
puts some_block.one
puts some_block.two
puts some_block.three
Is there a way to iterate over the values in the block?
First of all, the b parameter in the block is nil, so you will get a
NoMethodError: undefined method `one=' for nil:NilClass`
To fix this, you can change yield if block_given? to yield(self) if block_given?, which will pass self as the first parameter to the block.
If you want the b.one = ..., b.two = ... syntax, you should use an OpenStruct:
require 'ostruct'
class BlockClass < OpenStruct
def initialize
super
yield(self) if block_given?
end
end
You can get a dump of the internal Hash by calling marshal_dump:
some_block = BlockClass.new {|b|
b.one = 1
b.two = 2
b.three = 3
}
some_block.marshal_dump # => {:one=>1, :two=>2, :three=>3}
You can then iterate over the values:
some_block.marshal_dump.each_pair do |k, v|
puts "the value of #{k} is #{v}"
end
Your block takes 1 parameter, b, but your yield statement doesn't pass anything in. Perhaps you mean, yield self if block_given??
Also, if you want to "iterate", you'll need an enumerable collection of something, like an Array or Hash. As is, one, two, and three are totally unrelated accessors to your BlockClass.
You could iterate over all methods of BlockClass:
(some_block.methods).each do |method_name|
puts some_block.send(method_name)
end
But that doesn't sound like what you're looking for. Perhaps Initialize a Ruby class from an arbitrary hash, but only keys with matching accessors might help?

Iterate through an Ruby Array and get(yield or return) each item in the array

I have an array and I need to create a class method named "each" to yield or return (not sure what the difference is of those or which I need to use if any) each item in the array when the method is called.
Do I need to use return instead of yield or neither?
class Sum
def initialize
#sum = Array.new
end
def each
#sum.each do |item|
yield item
end
end
You could include Enumerable and implement each and get a lot of functionality for free.
class Sum
def initialize
#sum = []
end
def each &block
#sum.each &block
end
end
This will yield each item of the collection or if you do not provide a block it will return you an enumerator just like a normal Array would
Are you attempting to write a class that acts like an iterator? That provides it's own each form? If so, then this is the pattern for doing so with a ruby class:
class MyIterator
include Enumerable
def initialize data=[]
#data = data
end
def each
#data.each do |item|
yield item
end
end
end
m = MyIterator.new [1,2,3,4]
m.each do |item|
puts "item=#{item}"
end
puts m.map(&:next)
The for_each method iterates over the array (#data) and yields each of the values to the block.
As #jan-dvorak pointed out, including Enumerable and naming the method each gives additional benefits, such as being able to call map directly on the object.
Assuming that this is homework, and using the existing each method is not allowed:
class Sum
def initialize
#values = []
end
def homework_each
0.upto(#values.length-1){ |i| yield #values[i] }
end
end
However, your code as written works, modulo the fact that you're missing an end, and also you're missing any way to populate your array with values.

How to extend array methods in ruby?

Here is my code:
class Array
def anotherMap
self.map {yield}
end
end
print [1,2,3].anotherMap{|x| x}
I'm expecting to get an output of [1,2,3],but I get [nil,nil,nil]
What's wrong with my code?
class Array
def another_map(&block)
map(&block)
end
end
Your code doesn't yield the value that's yielded to the block you passed to #map. You need to supply a block parameter and call yield with that parameter:
class Array
def anotherMap
self.map {|e| yield e }
end
end
print [1,2,3].anotherMap{|x| x}

Calls block once for each element of an array, passing that element as a parameter

I have some difficulties for using Ruby block, passing in a method.
As in the following case, I would like to display each element of #array, from Box instance (using .each method):
class Box
def initialize
#array = [:foo, :bar]
end
def each(&block)
# well, hm..
end
end
a = Box.new
a.each { |element| puts element }
You really just need to delegate to the each method on #array and pass it the block. Additionally, you can include the Enumerable mix-in to gain access to the methods it provides (e.g. map, inject, etc...):
class Box
include Enumerable
def initialize
#array = [:foo, :bar]
end
def each(&block)
#array.each(&block)
end
end
More information on the Enumerable module is available in the documentation.
For this simple example, you actually aren't required to pass the block explicitly:
def each
#array.each{|e| yield e}
end
Passing the block (which is a Proc object) explicitly allows you to test it for things, like the number of arguments that it expects:
class Box
...
def each(&block)
#array.each do |e|
case block.arity
when 0
yield
when 1
yield e
when 2
yield e, :baz
else
yield
end
end
end
end
a = Box.new
a.each { puts "nothing" } # displays "nothing, nothing"
a.each { |e| puts e } # displays "foo, bar"
a.each { |e1, e2| puts "#{e1} #{e2}" } # displays "foo baz, bar baz"

Resources