From: Robert Klemme Date: 2004-08-31T05:25:26+09:00 Subject: Re: Flexible operations for a collection class "Edgardo Hames" schrieb im Newsbeitrag news:478c16ae040830065916ce7d7@mail.gmail.com... > 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. > > 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 Putting the comparison criteria into the elements is very bad design. What will you do if your elements are in two different collections at the same time? The sorting criterium belongs into the collection. > and I update every new object I add. If I had a Java Comparator like > class, that would be trivial. > > Can anybody point out some suggestions on how to do this in a better way. > Thanks for reading such a long message. Apparently you are trying to implement a sortable set. About the set part: They are already implemented in Ruby: >> require 'set' => true >> s=Set.new => # >> s << 1 << 2 << 3 << 1 => # >> s.sort => [1, 2, 3] >> s.sort {|a,b| b<=>a} => [3, 2, 1] >> About the sort part: Either use the built in functionality (like above) or make @sortable_by a lambda with two arguments like this: >> sortable_by = lambda {|a,b| b<=>a} => # >> s.sort &sortable_by => [3, 2, 1] The lambda is essentially the Comparator you know from Java. I'd probably do this if I wanted a sorted set class: require 'set' class SortedSet < Set DEF_SORT = lambda {|a,b| a <=> b} attr_accessor :comp def initialize(*args, &b) super(*args) @comp = b || DEF_SORT end def sort(&b) super( &( b || @comp ) ) end def each_sorted(&b) sort.each(&b) self end end Then you can do this: >> s = SortedSet.new => # >> s << 1 << 2 << 3 << 1 << 2 => # >> s.sort => [1, 2, 3] >> s.each_sorted {|x| puts x} 1 2 3 => # >> s.comp= lambda {|a,b| b<=>a} => # >> s.each_sorted {|x| puts x} 3 2 1 => # However, if you need to sort often compared to insert and delete operations, then a sorted data structure (like an ordered tree) is more efficient. Kind regards robert