我需要在Rails 3中对排名表进行排序,因此我有LeagueTable
模型,Team
模型和Match
模型。
基本排序是通过积分总结完成的,所以这很容易。但是当两支球队获得相同的分数时,我想按照这两支球队之间的比赛赢得的分数进行排序
我不知道该怎么做。
编辑:
# league_table.rb model
class LeagueTable < ActiveRecord::Base
belongs_to :team
end
# match.rb model
class Match < ActiveRecord::Base
belongs_to :team_home, :class_name => "Team"
belongs_to :team_away, :class_name => "Team"
end
# team.rb model
class Team < ActiveRecord::Base
has_many :matches
end
# schema.rb
create_table "league_tables", :force => true do |t|
t.integer "team_id"
t.integer "points"
t.integer "wins"
t.integer "draws"
t.integer "looses"
t.integer "goals_won"
t.integer "goals_lost"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "matches"
t.integer "priority"
end
create_table "matches", :force => true do |t|
t.integer "team_home_id"
t.integer "team_away_id"
t.integer "score_home"
t.integer "score_away"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "teams", :force => true do |t|
t.string "name"
t.datetime "created_at"
t.datetime "updated_at"
end
答案 0 :(得分:1)
我会这样做:
首先,我会根据他们的观点对球队进行排序。正如你所说,这是微不足道的。
然后,我将遍历已排序的数组并检查具有相同点的可能团队。这将显示在一个新的哈希数组中。对于我会发现具有相同点的每组球队,我会用哈希表示。想想3个竞争团队:
{ :TeamA => 3, :TeamB => 2 } { :TeamA => 3, :TeamC => 4 } { :TeamB => 1, :TeamC => 0 }
以max:
遍历1. max = TeamA
2. max = TeamC
最强大的团队是TeamC。消除该团队并重复。最后2个哈希现在被淘汰了,我们只剩下第一个,这表明TeamA&gt; TeamB。因此,最终的排序将是:
TeamC&gt; TeamA&gt; TeamB
通知:只有这两个被认为是TeamC并不比TeamB好。根据获胜点,该算法可以提供更好的团队。
你的情况实际上更简单。你只想比较两支球队。因此,哈希就像:
{ :TeamA => 3, :TeamB => 2 }
明确表示TeamA优于TeamB,应该排名更高。如果你想比较具有相同分数的3支球队,你必须有另一个标准,比如最高得分球队更好。
修改强>
如果获得最佳团队的下两个因素是得分并且然后失去差异,那么你会有另外一个哈希:
{ :TeamA => [3, 2], :TeamA => [2, 1], :TeamC => [1, 1] }
用[3,2]表示[进球得分,失去差距]。您现在可以根据这两个参数轻松识别最佳团队。
答案 1 :(得分:1)
我会在rival_points
模型中添加LeagueTable
列,并仅在其他团队拥有相同点数的情况下更新。
我在想这样的事情(没有测试它是否有效):
class LeagueTable
after_save :set_order_of_equals
def set_order_of_equals
LeagueTable.all.each do |lt|
points_against_rivals = 0
LeagueTable.where('points = ? and matches = ? and team_id <> ?', lt.points, lt.matches, lt.team_id).each do |lt_same_points|
points_against_rivals += lt.team.points_against(lt_same_points.team)
end
lt.rival_points = points_against_rivals
LeagueTable.after_save.clear # clear the after_save callback to prevent it from running endlessly
lt.save
end
end
end
class Team
def points_against(opponent)
points = 0
# Home games
matches.where(:team_away => opponent).each do |m|
if m.score_home == m.score_away
points += 1
elsif m.score_home > m.score_away
points += 3
end
end
# Away games
matches.where(:team_home => opponent).each do |m|
if m.score_away == m.score_home
points += 1
elsif m.score_away > m.score_home
points += 3
end
end
points
end
end
# With this you can get the correct order like this
lt = LeagueTable.order('points desc, matches asc, rival_points desc')
答案 2 :(得分:1)
这是一个SQL重要而有效的解决方案。大多数困难的逻辑都在执行 DB。
将一个名为group_rank
的新列添加到league_tables
表(默认为0)。在save
操作期间检查点碰撞。如果存在点碰撞,请计算碰撞团队的group_rank
。
这样,您可以使用简单的order
子句以正确的顺序获得团队。
LeagueTable.all(:order => "points ASC, group_rank ASC")
添加after_save
回调以确定LeagueTable
模型上的点碰撞。
# league_table.rb model
class LeagueTable < ActiveRecord::Base
belongs_to :team
after_save :update_group_rank
def update_group_rank
return true unless points_changed?
# rank the rows with new points and old points
rank_group(points) and rank_group(points_was)
end
#rank_group
方法:
def rank_group(group_points)
group_count = LeagueTable.count(:conditions =>{:points => group_points})
return true unless group_count > 1 # nothing to do
sql = "UPDATE league_tables JOIN
(
SELECT c.team_id, SUM(IF(c.score = 0, 1, c.score)) group_rank
FROM (
SELECT ca.team_home_id team_id, (ca.score_home-ca.score_away) score
FROM matches ca,
(SELECT cba.team_id
FROM league_tables cba
WHERE cba.points = #{group_points}
) cb
WHERE ca.team_home_id = cb.team_id AND ca.score_home >= ca.score_away
UNION
SELECT cc.team_away_id team_id, (cc.score_away-cc.score_home) score
FROM matches cc,
(SELECT cda.team_id
FROM league_tables cda
WHERE cda.points = #{group_points}
) cd
WHERE cc.team_away_id = cd.team_id AND cc.score_away >= cc.score_home
) c
GROUP BY c.team_id
) b ON league_tables.team_id = b.team_id
SET league_tables.group_rank = b.group_rank"
connection.execute(sql)
return true
end
end
确保在points
列上添加索引。
注意:此解决方案适用于MySQL。重写SQL以与其他数据库一起工作是相当直接的。
答案 3 :(得分:1)
非常有趣的问题。这是(更少)我将如何处理它,使用递归和group_by
的魔力。
{team_foo => {:points => 10, :goals_for => 33, :goals_against => 6}, team_bar => {:points => 18, :goals_for => 50, :goals_against => 11}...}
。final_order
,现在是空的。all_teams_scores
。group_by
:teams.group_by {|t| all_teams_scores[t][:points]}
对其进行分组。你得到的是OrderedHash
,看起来像是:{10 => [team_foo], 18 => [team_bar, team_xyz]}
。关键是你分组的,值总是一个数组。存储此哈希,例如为league_table
。league_table
的键进行排序(记得在需要时撤消)并根据它们检查league_table
的值。如果数组有两个或更多元素,则应用点3.-5。对阵列中的团队。如果数组有一个元素,请将此元素追加到final_order
。一些评论:
group_by
一个数组(当您想要在直接游戏排序之前或之后对多种事物进行排序时,这会有所帮助。)create view
基于此,并通过它访问Rails的联盟表(视图一般, 只读)。当然,对于99.99%的情况,从管理界面中删除对这些字段的访问也是可以的;)希望这有帮助。
答案 4 :(得分:0)
我认为使用数据库排序很难做到这一点。可以使用团队之间的点来预先计算交叉表,但是如何在搜索中使用它。我想如果三支球队并列,问题就更难了。
对数据库中的内容进行排序。然后再次浏览列表以理清关系。您必须计算受影响团队的决胜局。您可以将团队从上到下放入一个新阵列,或者向团队添加一个抢七栏,并在第二轮中使用。
我会在你的模型类中添加一个方法来解决关系中断并返回一个已排序的数组。
这项任务非常重要,但可能很有趣。
答案 5 :(得分:0)
你的问题听起来很有趣。我参加了很多比赛(排球),如果得分相等(根据赢或输的比赛赢得积分),得分最多的队伍和最少的对位将先排序。因此,在这种情况下,不是真正关注单个匹配,而是按总赢得目标和-lost排序。
那么这很简单:
LeagueTable.all.order('points desc, goals_won desc, goals_lost desc')
我认为这将是一个相当不错的订单。然后选择最具攻击性的球队。您还可以通过order('points desc, goals_lost desc, goals_won desc')
排序来选择最具防御性的团队。
但这只是一条捷径。我想你不能随心所欲地改变排序规则,这是非常好的问题。以下是我将如何处理它的一些代码
all_teams_simple_sort = LeagueTable.all.sort('points desc')
all_teams_sorted = []
teams_equal_points = []
prev_team = all_teams_simple_sort[1]
prev_points = all_teams_simple_sort[1].points
(2..all_teams_simple_sort.size).each do |team_index|
team = all_teams_simple_sort[team_index]
if team.points == prev_team.points
teams_equal_points << prev_team if teams_equal_points.size == 0
teams_equal_points << team
else
if teams_equal_points.size > 0
add_equals_sorted_to all_teams_sorted, teams_equal_sorted
teams_equal_sorted = []
else
all_teams_sorted << prev_team
end
all_teams_sorted << team
end
prev_team = team
end
这应该遍及所有团队,将所有团队合并到相同的点,并在需要时添加所有其他团队。
现在我们只需要编写最难的函数add_equals_sorted_to
,它将以正确的顺序将团队的等点添加到结果排序中。
def add_equals_sorted_to(result_sorted, equals_unsorted)
team_ids = equals_unsorted.collect(&:team_id)
# get all the matches for between those teams
matches = Match.where('team_home_id in (?) and team_away_id in (?)', team_ids.join(','), team_ids.join(','))
# create an empty hash for each team
team_score = {}
team_ids.each {|id| team_scores[id] = {:won => 0, :lost => 0} }
matches.each do |match|
team_scores[match.team_home_id] = {:won => team_scores[match.team_home_id][:won] + match.score_home, :lost => team_scores[match.team_home_id][:lost] + match.score_away }
team_scores[match.team_away_id] = {:won => team_scores[match.team_away_id][:won] + match.score_away, :lost => team_scores[match.team_home_id][:lost] + match.score_home }
end
# get the team with the highest :won and add to result_sorted
# and repeat until no more left
end
此代码未经过测试:)但我希望它能让您入门。