0

I am trying to create my own each method for the array class. Code is as follows:

class Array

  def my_each
    i = 0
    while i < self.length
      yield(self[i])
      i += 1
    end
    self
  end
end

my_each {|i| puts i}



[1,2,3,4].my_each

This seems to me like it should work correctly. However, I am getting the message

"Undefined method my_each for main: Object"

I tried moving the block inside the Array class under the method but then got the following:

"Undefined method my_each for Array Class"

My block and method have the same name so it seems to me like this would be an issue of scope. How exactly do blocks behave with method is being defined within a class? Is it advisable to do something like this or simply include the block directly inside my method?

John
  • 229
  • 2
  • 10

4 Answers4

3

Since you've defined it in the Array context, you need to call it that way as well:

[1,2,3,4].my_each do |i|
  puts i
end

That calls the my_each method defined on an Array instance. You even have this later in your code, but the earlier error blocked it.

If you got that second error it's because you tried:

Array.my_each([1,2,3,4])

That's not correct, you'd need to define a class method with the self. prefix if you wanted that to work.

tadman
  • 208,517
  • 23
  • 234
  • 262
1
class Array
  def my_each
    to_enum
  end
end

enum = [1,2,3,4].my_each 
  #=> #<Enumerator: [1, 2, 3, 4]:each> 
enum.to_a
  #=> [1, 2, 3, 4] 
enum.map { |n| n*2 }
  #=> [2, 4, 6, 8]
enum.next
  #=> 1 
enum.next
  #=> 2 
enum.next
  #=> 3 
enum.next
  #=> 4 
enum.next
  #=> StopIteration: iteration reached an end

Array.instance_method(:to_enum).owner
  #=> Kernel 

but Kernel's instance methods are documented in Object, so see Object#to_enum.

Cary Swoveland
  • 106,649
  • 6
  • 63
  • 100
  • Is it possible to use map in this form in a scope? I have a array of arrays, and want to work with in a scope. – Aboozar Rajabi Nov 12 '16 at 16:56
  • @AboozarRajabi, can you give me an example? – Cary Swoveland Nov 12 '16 at 18:33
  • I have an array stored in a field like this: [["2","3"],["4","1"]], suppose that the first elements are user ids (2 , 4), and the second ones are number of their siblings, I want to search for a specific id to see if it's in the array or not, and if it's in the array, what's the second element related to this id?! – Aboozar Rajabi Nov 12 '16 at 18:42
  • @AboozarRajabi, that doesn't seem to have much to do with this question, but if `arr = [["2","3"],["4","1"]]` and `target_id="4"`, that would just be `_, sibs = arr.find { |id,_| id==target_id }; sibs`. If `arr` does not have an element `[id, sibs]` for which `id==target_id`, `sibs` will equal `nil`. – Cary Swoveland Nov 13 '16 at 03:26
  • See this question plz: http://stackoverflow.com/questions/40614678/rails-how-to-use-scope-to-find-an-element-in-array-of-arrays – Aboozar Rajabi Nov 16 '16 at 11:16
  • @Aboozar, sorry but I don't know Rails. – Cary Swoveland Nov 16 '16 at 17:38
0

This has nothing to do with scope, blocks, or yield. You have defined your method in the Array class, which means that it is available for all Arrays, but you aren't calling it on an Array, you are calling it on main, which is a direct instance of Object, not Array.

I tried moving the block inside the Array class under the method but then got the following:

"Undefined method my_each for Array Class"

In that case, you are calling it on Array, which is a direct instance of Class and also not an Array.

My block and method have the same name

Blocks don't have names.

[1,2,3,4].my_each

Here, you are calling the method on an Array, but your method requires a block, which you forgot to pass. Therefore, you will get a different error message.

Jörg W Mittag
  • 363,080
  • 75
  • 446
  • 653
0

i guess the best way is to do this:

Array.class_eval do
  def my_each
    # do here what ever you want 
    #( you can us self if you want to handle the current object)
  end
end

[1,3,3,4].my_each

or use this reference for best answer. hope i it will help you

Community
  • 1
  • 1
Somar Melkhem
  • 238
  • 2
  • 10