patternrubyMinor
Small football table builder in Ruby
Viewed 0 times
builderfootballsmallrubytable
Problem
Please review this small program I wrote to create a World Cup Group Table. You feed the program match info, and it builds a table based on that info.
The rules are a win earns three points, a draw one point, a loss zero points.
For example, if we feed the method this match data:
```
[
{ id: 1, home_team: "Honduras", away_team: "Chile", home_score: 0, away_score: 1 },
{ id: 2, home_team: "Spain", away_team: "Switzerland", home_score: 0, away_score: 1 },
{ id: 3, home_team:
def table_for(matches)
# Defaults that each team in the table should have
defaults = { goals_for: 0, points: 0 }
# Using the matches, let's create a hash for each team involved in a match
teams = matches.each_with_object([]) do |match, array|
array.push({ name: match[:home_team] }.merge!(defaults))
array.push({ name: match[:away_team] }.merge!(defaults))
end
# We have 6 matches; each match involves 2 teams; we have 12 teams in our array.
# Each team plays 3 matches, so we have 4 unique.
teams.uniq
# Loop through matches to build the table.
matches.each do |match|
# We only want to calculate data for matches that were played
if match[:home_score].present? && match[:away_score].present?
# Grab the teams for this particular match from the list of teams
home_team = teams.detect { |team| team[:name] == match[:home_team] }
away_team = teams.detect { |team| team[:name] == match[:away_team] }
# Compute goals_for
home_team[:goals_for] += match[:home_score]
away_team[:goals_for] += match[:away_score]
# Home team wins
if match[:home_score] > match[:away_score]
home_team[:points] += 3
# Away team wins
elsif match[:home_score] < match[:away_score]
away_team[:points] += 3
# It's a draw
else
home_team[:points] += 1
away_team[:points] += 1
end
end
end
teams
endThe rules are a win earns three points, a draw one point, a loss zero points.
For example, if we feed the method this match data:
```
[
{ id: 1, home_team: "Honduras", away_team: "Chile", home_score: 0, away_score: 1 },
{ id: 2, home_team: "Spain", away_team: "Switzerland", home_score: 0, away_score: 1 },
{ id: 3, home_team:
Solution
Note that
uniq is not in-place, uniq! is. Otherwise, I guess your code is pretty good if you like imperative style (maybe some unnecessary comments could be removed, though). Personally, I prefer a functional approach: less statements, more expressions. If you open a console and play with the input data, applying transformations, you'll see it's not as difficult as it would seem at first. In this case I'd write:goals_cmp_to_points = {-1 => 0, 0 => 1, 1 => 3}
team_results = match_results.flat_map do |result|
[[:home, :away], [:away, :home]].map do |side1, side2|
goals_cmp = result[:"#{side1}_score"] result[:"#{side2}_score"]
{
:team => result[:"#{side1}_team"],
:goals => result[:"#{side1}_score"],
:points => goals_cmp_to_points[goals_cmp],
}
end
end
#[{:team=>"Honduras", :goals=>0, :points=>0},
# {:team=>"Chile", :goals=>1, :points=>3},
# [...]
# {:team=>"Switzerland", :goals=>0, :points=>1}]
table = team_results.group_by { |r| r[:team] }.map do |team, results_for_team|
{
:team => team,
:total_goals => results_for_team.map { |r| r[:goals] }.reduce(0, :+),
:total_points => results_for_team.map { |r| r[:points] }.reduce(0, :+),
}
end
sorted_table = table.sort_by { |tr| tr.values_at(:total_points, :total_goals) }.reverse
#[{:team=>"Spain", :total_goals=>4, :total_points=>6},
# {:team=>"Chile", :total_goals=>3, :total_points=>6},
# {:team=>"Switzerland", :total_goals=>1, :total_points=>4},
# {:team=>"Honduras", :total_goals=>0, :total_points=>1}]Code Snippets
goals_cmp_to_points = {-1 => 0, 0 => 1, 1 => 3}
team_results = match_results.flat_map do |result|
[[:home, :away], [:away, :home]].map do |side1, side2|
goals_cmp = result[:"#{side1}_score"] <=> result[:"#{side2}_score"]
{
:team => result[:"#{side1}_team"],
:goals => result[:"#{side1}_score"],
:points => goals_cmp_to_points[goals_cmp],
}
end
end
#[{:team=>"Honduras", :goals=>0, :points=>0},
# {:team=>"Chile", :goals=>1, :points=>3},
# [...]
# {:team=>"Switzerland", :goals=>0, :points=>1}]
table = team_results.group_by { |r| r[:team] }.map do |team, results_for_team|
{
:team => team,
:total_goals => results_for_team.map { |r| r[:goals] }.reduce(0, :+),
:total_points => results_for_team.map { |r| r[:points] }.reduce(0, :+),
}
end
sorted_table = table.sort_by { |tr| tr.values_at(:total_points, :total_goals) }.reverse
#[{:team=>"Spain", :total_goals=>4, :total_points=>6},
# {:team=>"Chile", :total_goals=>3, :total_points=>6},
# {:team=>"Switzerland", :total_goals=>1, :total_points=>4},
# {:team=>"Honduras", :total_goals=>0, :total_points=>1}]Context
StackExchange Code Review Q#51950, answer score: 3
Revisions (0)
No revisions yet.