How to extend array methods in ruby? - 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}

Related

How to use a defined method in another method

I'm wondering if you can use a defined method into another defined method
like for example
def method1(example)
funtion1
end
def method2(example)
funtion2
end
Like how can you use method1 to method2
def method_1(arg)
arg.call
end
def method_2
puts 'hi'
end
method_1(method(:method_2)) #=> should print 'hi'
You can't pass a method to a method. However you can pass a proc to a method.
Define a proc like so
proc = Proc.new {|x| puts x}

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?

Ruby - Can variable set equal to object of custom class transfer custom class to the variable?

class New_Class
def initialize
#array = ['1A','2A','1B','2C']
end
def remove_letters
#array.map do |rmv|
rmv.sub /([0-9])([A-Z])/, '\1'
end
end
def show_array
#array.each do |element|
puts element
end
end
end
array = New_Class.new
new_array = array.remove_letters
puts new_array.class #=> Array
new_array.each do |element|
puts element #=>1,2,1,2
end
array.show_array #=>1A,2A,1B,2C
new_array.show_array #=>Undefined method error since it's an object of type Array not New_Class
How would I make new_array an object of type New_Class instead of Array?
This doesn't work:
new_array = New_Class.new
new_array = array.remove_letters
Still says new_array is of type Array.
I played around with clone and dup but couldn't get it to work that way either. Thanks!
Your remove_letters method returns an array, which you then assign to new_array. If you want new_array to be another instance of New_Class instead, then you need to return another instance of NewClass from your remove_letters method, not an array:
def initialize(values = ['1A','2A','1B','2C'])
#array = values
end
def remove_letters
New_Class.new(#array.map { |rmv| rmv.sub(/([0-9])([A-Z])/, '\1') })
end
Above, I modify initialize to use your array as a default, if an initial array value isn't passed to New_Class.new, then it uses ['1A','2A','1B','2C'] as a default.

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.

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

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

Resources