%w(Ruby Python Javascript).sort => ["Javascript", "Python", "Ruby"]or
%w(Ruby Python Javascript).sort_by(&:reverse) => ["Python", "Javascript", "Ruby"]But what about a simple class like:
class Food attr_accessor :name, :kcal # acronym for kilocalories def initialize name, kcal @name = name @kcal = kcal end endAssuming the foods collection:
foods = [ Food.new('Pear', 57), Food.new('Apple', 52), Food.new('Avocado', 164) ]could be sorted with Enumerable#sort_by:
foods.sort_by(&:name) => [ #<Food @name="Apple", @kcal=52>, #<Food @name="Avocado", @kcal=164>, #<Food @name="Pear", @kcal=57> ]but can not with not be sorted with Enumerable#sort:
foods.sort => ArgumentError: comparison of Food with Food failedbecause Ruby expects a sorting method in the Food class definition. Passing the sorting block works:
foods.sort { |a, b| a.name <=> b.name } => [ #<Food @name="Apple", @kcal=52>, #<Food @name="Avocado", @kcal=164>, #<Food @name="Pear", @kcal=57> ]in which String#<=> satisfies the sorting expectation.
The sorting logic is worth a move into the class definition itself:
class Food attr_accessor :name, :kilocalories def initialize name, kilocalories # acronym for kilocalories @name = name @kilocalories = kilocalories end def <=> other name <=> other.name end endwhich expects an object responding to a name method, which obviously should return a String. It works like:
foods.sort => [ #<Food @name="Apple", @kcal=52>, #<Food @name="Avocado", @kcal=164>, #<Food @name="Pear", @kcal=57> ]A more complicated sorting requirements can be defined like:
class Food attr_accessor :name, :kcal def initialize name, kcal @name = name @kcal = kcal end def <=> other return -(kcal <=> other.kcal) if name.first.eql? other.name.first name <=> other.name end endwhich compares the foods by their name, but if the first letter is equal, then sort compares them by their kilocalories but in descending order. Descending is achieved by inverting comparison value, which can be negative, zero or positive.
The rule (whereas 'a' is self and 'b' is the other):
- if a < b then return -1
- if a = b then return 0
- if a > b then return 1
- if a and b are not comparable then return nil
[ Food.new('Pear', 57), Food.new('Apple', 52), Food.new('Avocado', 164) ].sort => [ #<Food @name="Avocado", @kcal=164>, #<Food @name="Apple", @kcal=52>, #<Food @name="Pear", @kcal=57> ]Please note that Avocado is before Apple, just because of its higher kilocalories.
Supported by Ruby 2.1.1
Keine Kommentare:
Kommentar veröffentlichen