krystah
krystah

Reputation: 3733

Defining a function to act upon an array

In Ruby, we define class member functions like this

class Dog
  def bark
    puts "woof"
  end
end

What I want to know and have been thoroughly unsuccessful in googling is, how and where does one define methods to act upon object-arrays in Ruby?

I wish to be able to do something like this:

dogs = [Dog.new, Dog.new, Dog.new]
dogs.some_function

How and where do I define some_function?

Note: I am not after solutions to a specific problem, more so the steps in how to define such a function in general.

Upvotes: 0

Views: 94

Answers (3)

engineersmnky
engineersmnky

Reputation: 29308

You could also create a class that inherits from Array such as

class DogWalker < Array
  def some_function
    self.each(&:bark)
  end
end

class Dog
  def bark
    puts "woof"
  end
end

d = DogWalker.new([Dog.new,Dog.new,Dog.new])
d.some_function 
#woof
#woof
#woof 
#=> [#<Dog:0x2a5a2e8>, #<Dog:0x2a5a2d0>, #<Dog:0x2a5a2b8>]

This means that you can only call this method on an instance of DogWalker (as well as all the methods available to Array) but does not alter Array itself. Giving you better control of the objects included in it. e.g.

class DogWalker < Array
  def initialize(args)
    raise ArgumentError.new("Must be an Array of Dogs") unless args.is_a?(Array) && args.all?{|e| e.is_a?(Dog)}
    super
  end
end

d = DogWalker.new([Dog.new,Dog.new,Dog.new])
#=>[#<Dog:0x2a5a2e8>, #<Dog:0x2a5a2d0>, #<Dog:0x2a5a2b8>]
d = DogWaler.new([Dog.new,12])
#=>ArgumentError: Must be an Array of Dogs
Array.new([1,2,3])
#=>[1,2,3]

As @UriAgassi pointed out very rarely do you need to alter base classes especially if you are designing extended functionality that does not need to/cannot apply across object types. Here is an example:

class Array
  def some_function
    self.map(&:bark)
  end
end
class Dog
  def bark
    "woof"
  end
end
d = [Dog.new,Dog.new]
d.some_function
#=> ["woof","woof"]
d = [1,2,3,4]
d.some_function
#=>NoMethodError: undefined method `bark' for 1:Fixnum

Since Array can accept any object type the Array is fine but the some_function method requires an object that responds_to bark which will raise in the event the object cannot perform the task requested.

Upvotes: 1

ceth
ceth

Reputation: 45285

In your example 'dogs' is the instance of Array class. If you want to extend Array class you can add method to it like this:

class Array
  def some_function
  end
end

Upvotes: 0

Uri Agassi
Uri Agassi

Reputation: 37409

To make all your dogs bark, you should use each on the array:

dogs.each(&:bark)

which is equivalent to

dogs.each { |dog| dog.bark }

Very rarely you need to define a method on an array, in which case it will be available on all arrays, containing anything. To do that, you need to declare it inside the Array class, by declaring it again:

class Array
  def some_function
    # do something...
  end
end

And then you could run:

dogs = [Dog.new, Dog.new, Dog.new]
dogs.some_function

and also

numbers = [1, 2, 3, 4]
numbers.some_function

Upvotes: 5

Related Questions