Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to pass a custom comparator to "sort"?

Class A has the following comparator:

class A   attr_accessor x    def my_comparator(a)     x**2 <=> (a.x)**2   end end 

I would like to use this comparator to sort an array where each item is of class A:

class B   def my_method     items.sort!(<how can I pass my_comparator here ?>)   end end 

How should I pass my_comparator to sort!?

like image 332
Misha Moroshko Avatar asked Jan 07 '11 03:01

Misha Moroshko


People also ask

How do I sort a custom comparator?

Using a comparator, we can sort the elements based on data members. For instance, it may be on roll no, name, age, or anything else. Method of Collections class for sorting List elements is used to sort the elements of List by the given comparator.

How do you implement a comparator in Python?

Comparator functions are deprecated in Python; use key functions instead. if condition: return True else: return False should be return condition . Dictionaries do not preserve order. If you want a sorted dictionary you should use OrderedDict from the collections module.

How do you create a custom sort in Java?

In order to sort Employee object on different criteria, we need to create multiple comparators e.g. NameComparator, AgeComparator, and SalaryComparator, this is known as custom sorting in Java. This is different from the natural ordering of objects, provided by the compareTo() method of java.


2 Answers

Define your own <=>, and include Comparable. This is from the Comparable doc:

class SizeMatters   include Comparable   attr :str   def <=>(an_other)     str.size <=> an_other.str.size   end   def initialize(str)     @str = str   end   def inspect     @str   end end  s1 = SizeMatters.new("Z") s2 = SizeMatters.new("YY") s3 = SizeMatters.new("XXX") s4 = SizeMatters.new("WWWW") s5 = SizeMatters.new("VVVVV")  s1 < s2                       #=> true s4.between?(s1, s3)           #=> false s4.between?(s3, s5)           #=> true [ s3, s2, s5, s4, s1 ].sort   #=> [Z, YY, XXX, WWWW, VVVVV] 

You don't actually have to include Comparable, but you get extra functionality for free if you do that after having defined <=>.

Otherwise, you can use Enumerable's sort with a block if your objects implement <=> already.

Another way to use several different comparisons is to use lambdas. This uses the new 1.9.2 declaration syntax:

ascending_sort  = ->(a,b) { a <=> b } descending_sort = ->(a,b) { b <=> a }  [1, 3, 2, 4].sort( & ascending_sort ) # => [1, 2, 3, 4] [1, 3, 2, 4].sort( & descending_sort ) # => [4, 3, 2, 1]  foo = ascending_sort [1, 3, 2, 4].sort( & foo ) # => [1, 2, 3, 4] 
like image 193
the Tin Man Avatar answered Oct 14 '22 16:10

the Tin Man


Both of these should work:

items.sort_by! { |a| (a.x)**2 } items.sort! { |a1,a2| a1.my_comparator(a2) } 
like image 40
Sophie Alpert Avatar answered Oct 14 '22 18:10

Sophie Alpert