Overloading sort_by (blocks question)

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

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:

<snip>

   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

···

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

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

···

On 5/16/07, Dan Zwell <dzwell@gmail.com> wrote:

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

--
Rick DeNatale

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

Thanks, that worked immediately.

Dan

Mark Day wrote:

···

On May 16, 2007, at 1:49 PM, Dan Zwell 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:

<snip>

   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

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

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