Overloading sort_by (blocks question)


#1

Hi, all,

I have subclassed Array to add some features. I am redefining sort_by so
that it does not return an Array but a Pairs (my subclass), but I cannot
get the syntax for passing the blocks right.

Here’s my code:

class Pairs < Array
def initialize(ary)
self.replace(ary)
end

 alias_method :sort_by_old, :sort_by
 # PROBLEM here:
 def sort_by(&block)
   ary = self.sort_by_old {yield(block)}
   Pairs.new(ary)
 end

end

I just want Pairs#sort_by to return a Pairs object every time it is
called. Could anybody help me?

Thanks,
Dan


#2

On May 16, 2007, at 1:49 PM, Dan Z. wrote:

I have subclassed Array to add some features. I am redefining
sort_by so that it does not return an Array but a Pairs (my
subclass), but I cannot get the syntax for passing the blocks right.

Here’s my code:

alias_method :sort_by_old, :sort_by

PROBLEM here:

def sort_by(&block)
ary = self.sort_by_old {yield(block)}
Pairs.new(ary)
end

Use this instead:
ary = self.sort_by_old(&block)

p=Pairs[“abcd”, “efg”, “hi”, “j”]
=> [“abcd”, “efg”, “hi”, “j”]

p.class
=> Pairs

q=p.sort_by{|x| x.length}
=> [“j”, “hi”, “efg”, “abcd”]

q.class
=> Pairs

-Mark


#3

Thanks, that worked immediately.

Dan


#4
      @array = array
 end

 def to_pairs

Thanks for the code examples–I previously didn’t understand exactly how
delegation worked. This looks neat. I’ll be playing with this and will
most likely use it.

Thanks again,
Dan


#5

On 5/16/07, Dan Z. removed_email_address@domain.invalid wrote:

   self.replace(ary)

I just want Pairs#sort_by to return a Pairs object every time it is
called. Could anybody help me?

Thanks,
Dan

def sort_by(&block)
Pairs.new(self.sort_by_old(&block))
end

This should solve your block passing problem.

However, I’d strongly suggets rethinking making a subclass of array
In most cases its better to use delegation to an array instance
variable.

Something like:

require ‘forwardable’

class Pairs
extend Forwardable

  # For methods which you would not have overriden in the subclass
  # simply delegate to the instance variable @array
  # this is just an example, the division between methods which are
  # simply delegated, which are replaced and which are wrapped is up
  # to you.
  def_delegators(:@array, :[], :[]=,  :+ #...

  # I notice that your initialize method doesn't seem to take the 

same
# argument as Array so …
def initialize(array)
@array = array
end
=begin
# If you wanted to have the same protocol for creating your class
# as Array then you’d need something like this.
def initialize(*args, &block)
@array = Array.new(*args, &block)
end

  def self.[](*objs)
       new().replace(Array[*objs])
  end

=end

  def replace(other_array)
       self.array = other_array.to_ary  # use to_ary instead of to_a
  end

  # to_ary is used for internal conversions of objects
  def to_ary
       array
  end

  # For a method which was replaced in your subclass just replace
  # explicit (or implicit) self with @array
  def concat(other_arr)
        array.concat(other_arr.to_pairs)
  end

  def sort_by(&b)
        Pairs.new(@array.sort_by(&b))
  end

  def to_pairs
      self
  end

end

class Array
def to_pairs
Pairs.new(self)
end
end


Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/