From: George Ogata Date: 2004-08-31T01:40:31+09:00 Subject: Re: Flexible operations for a collection class Edgardo Hames writes: > Hi you all. > > I'm writing a collection class, which looks like this (by heart, don't > have the code here) > > class Collection > def initialize(elements=nil) > @elements=elements > end > def add(elements) > elements.each{ |o| @elements << o unless @elements.include? o} > end > def sortable_by=(element_attribute) > @sortable_by=element_attribute > end > end > > I need some help with the following issues: > > 0) I would like to be able to pass either an array or an single object > to the constructor, but (a) @elements should be a list of objects (not > an array containing arrays). I tried this > > def initialize(o, *array) > @elements << o > array.each{ |o| @elements << o unless @elements.include? o} > end > > but (a) is not satisfied when I do > > c = Collection.new([1,2,3,4]) > > The same goes for the #add method. It seems like a bad idea to me to have the constructor take _either_ n element-args, _or_ 1 list-of-elements-arg, since, as you discovered, the "argument spaces" overlap. I'd suggest doing it like Array: -- Collection#initialize may take an Enumerable, in which case its elements become the elements of the new Collection -- Collection.[] takes n element args, and returns the n-element Collection containing those elements Thus, Collection.new(list) is the same as Collection[*list]. > 1) I would like to use the #sortable_by method to indicate which > attribute of the collection elements should be compared when sorting > it. Right now, I'm sorting it like this > > def sort(&block) > if block_given? > @elements.sort(&block) > else > @elements.sort{|x,y| x.send(@sortable_by) <=> y.send(@sortable_by)} > end > end > > but I would like to implement the #<=> method in my objects, so I can > delegate the #sort method to @elements. I came up with this, > > def sortable_by=(element_attribute) > @sortable_by=element_attribute > @elements.each{|o| o.sortable_by=@sortable_by} > end > > and I update every new object I add. If I had a Java Comparator like > class, that would be trivial. My suggestion: If the ordering is part of the elements' nature, then implement the elements' class's #<=> accordingly. If the ordering is something that belongs to the container (Collection), then have Collection#sort_by (just like Array#sort_by). If you want the sort proc to be part of the container's state, then do something like (untested): class Collection attr_accessor :default_sort_proc def sort &blk blk ||= default_sort_proc @elements.sort(&blk) end ## optional def default_sort_key= proc self.default_sort_proc = lambda{|x, y| proc.call(x) <=> proc.call(y)} end end HTH.