Here's another approach, which, although somewhat complex, has been developed to increase efficiency. The method uses the following steps.
- Convert each instance of
Team into an array containing an instance and an array of three elements, on which low-cost sorting will be performed. - Use Enumerate # sort_by to sort arrays using three-element arrays.
- Use Enumerable # chunk to group two-element arrays with equal three-element arrays.
- Match each element of the selected array with the
Team instance in the two-element array. - Use List # flat_map to match each selected group of
Team instances after sorting by a_beat_b(a, b) (if only the group contains only one command, of course).
code
def sort_em(teams) teams.map { |t| [t, [t.points, t.goal_dif, t.goals]] }. sort_by(&:last). chunk(&:last). map { |_,tied_teams| tied_teams.map(&:first) }. flat_map { |tied_teams| (tied_teams.size == 1) ? tied_teams.first : tied_teams.sort { |a,b| a_beat_b(a, b) } } end
Example
class Team attr_reader :name, :points, :goal_dif, :goals def initialize(name, points, goal_dif, goals) @name, @points, @goal_dif, @goals = name, points, goal_dif, goals end end teams = [Team.new("bluebirds", 233, 25, 130), Team.new("eagles", 233, 18, 105), Team.new("jays", 233, 25, 130), Team.new("owls", 160, 12, 105), Team.new("sparrows", 233, 18, 105) ] #=> [#<Team:0x007ff2f900e5a8 @name="bluebirds", @points=233, @goal_dif=25, @goals=130>, # #<Team:0x007ff2f900e530 @name="eagles", @points=233, @goal_dif=18, @goals=105>, # #<Team:0x007ff2f900e4b8 @name="jays", @points=233, @goal_dif=25, @goals=130>, # #<Team:0x007ff2f900e440 @name="owls", @points=160, @goal_dif=12, @goals=105>, # #<Team:0x007ff2f900e3c8 @name="sparrows", @points=233, @goal_dif=18, @goals=105>] def a_beat_b(a, b) a.name.size <=> b.name.size end sort_em(teams) #=> [#<Team:0x007ff2fa845630 @name="owls", @points=160, @goal_dif=12, @goals=105>, # #<Team:0x007ff2fa845720 @name="eagles", @points=233, @goal_dif=18, @goals=105>, # #<Team:0x007ff2fa8455b8 @name="sparrows", @points=233, @goal_dif=18, @goals=105>, # #<Team:0x007ff2fa8456a8 @name="jays", @points=233, @goal_dif=25, @goals=130>, # #<Team:0x007ff2fa8457e8 @name="bluebirds", @points=233, @goal_dif=25, @goals=130>]
Explanation
Following are the steps.
a = teams.map { |t| [t, [t.points, t.goal_dif, t.goals]] }
To find out what values ββare generated by the enumerator c , we can convert it to an array.
c.to_a #=> [[[160, 12, 105], # [[#<Team:0x007ff2fa845630 @name="owls",@points=160,@goal_dif=12,@goals=105>, # [160, 12, 105] # ] # ] # ], # [[233, 18, 105], # [[#<Team:0x007ff2fa845720 @name="eagles",@points=233,@goal_dif=18,@goals=105>, # [233, 18, 105] # ], # [#<Team:0x007ff2fa8455b8 @name="sparrows",@points=233,@goal_dif=18,@goals=105>, # [233, 18, 105] # ] # ], # [[233, 25, 130], # [[#<Team:0x007ff2fa8457e8 @name="bluebirds",@points=233,@goal_dif=25,@goals=130>, # [233, 25, 130] # ], # [#<Team:0x007ff2fa8456a8 @name="jays", @points=233,@goal_dif=25,@goals=130>, # [233, 25, 130] # ] # ] # ] # ]
d = c.map { |_,tied_teams| tied_teams.map(&:first) } #=> [[#<Team:0x007ff2fa845630 @name="owls", @points=160, @goal_dif=12, @goals=105>], # [#<Team:0x007ff2fa845720 @name="eagles", @points=233, @goal_dif=18, @goals=105>, # #<Team:0x007ff2fa8455b8 @name="sparrows", @points=233, @goal_dif=18, @goals=105> # ], # [#<Team:0x007ff2fa8457e8 @name="bluebirds", @points=233, @goal_dif=25, @goals=130>, # #<Team:0x007ff2fa8456a8 @name="jays", @points=233, @goal_dif=25, @goals=130> # ] # ] d.flat_map { |tied_teams| (tied_teams.size == 1) ? tied_teams.first : tied_teams.sort { |a,b| a_beat_b(a, b) } } #=> [#<Team:0x007ff2fa845630 @name="owls", @points=160, @goal_dif=12, @goals=105>, # #<Team:0x007ff2fa845720 @name="eagles", @points=233, @goal_dif=18, @goals=105>, # #<Team:0x007ff2fa8455b8 @name="sparrows", @points=233, @goal_dif=18, @goals=105>, # #<Team:0x007ff2fa8456a8 @name="jays", @points=233, @goal_dif=25, @goals=130>, # #<Team:0x007ff2fa8457e8 @name="bluebirds", @points=233, @goal_dif=25, @goals=130> # ]