Bas
Bas

Reputation: 11

Looping a text file creating a nested hash from data and updating the hash

I am trying to process a series of sports results that get supplied as a text file. Each line contains the name of the home team, the name of the away team, and the result. I need to then update a league table to show a number of stats for each team.

I was initially trying it as a class but think it might be simpler as a nested hash, as I don't think I can set new objects without knowing the team names, and this could change later, for example if I get data in for a new team playing I think it's easier to add it to a hash as this would hopefully be a unique name.

None of the answers I have found here seem to fix my issue. Is this because I am going about it the wrong way?

example input:

``file =<<~MSG
Team A, Team B, win
Team C, Team A, draw
Team C, Team B, lose
...
MSG``

so I read each line:

``file.each_line do |score_line|
  match_info = score_line.delete!("\n").split(';') # [<home>,<away>,<score>]
  home = match_info[0]
  away = match_info[1]
  score = match_info[2]
...
end``

I now have each team and the score to play with. This is where I am stumped. I am trying to create a nested hash the first level being the team name the next level being the stats.

For example:

``=> { 'Team A' => {:matches_played=> 2, :wins=> 1, :draw=> 1, lose=> 0},
  'Team B' => {:matches-played=> 2, :wins=> 1, :draw=> 0, lose=> 1},
...``

If I create a simple hash of one level, (e.g. Team => matches played) I have no trouble updating the hashes. For some reason as soon as I try to update the second level I get problems.

As the info is coming in I need to update the team stats, this could be as the home or away team, so I am using the variables home and away from the above example. To avoid re-setting the initial values of the hash, I have tried using #unless which just returns an empty hash. For example:

``  unless table.has_key?(home)
    table[home] = {:matches_played=> 1, :wins=> 0, :draw=> 0, lose=> 0}
  end
  unless table.has_key?(away)
    table[away] = {:matches_played=> 1, :wins=> 0, :draw=> 0, lose=> 0}
  end``

the idea was to then update the values of the keys in the second level depending on the score.

Currently I can't even get the second hash implemented, although the programme runs without errors, when I print out the hash I get => {}. Without the #unless the hash just keeps getting the initial values reset with each line read.

I am new to Ruby but feel that I will always need to create/access nested hashes outside of a database so trying to learn.

Upvotes: 1

Views: 28

Answers (1)

Stefan
Stefan

Reputation: 114188

Assuming that after processing your file / input, you have an array like this:

results = [
  ["Team A", "Team B", "win"],
  ["Team C", "Team A", "draw"],
  ["Team C", "Team B", "lose"]
]

You could create a hash using a default proc which sets the defaults for a new entry:

table = Hash.new { |h, k| h[k] = { matches_played: 0, wins: 0, draw: 0, lose: 0 } }

Now, you can process the above results in a loop like this:

results.each do |home, away, score|
  table[home][:matches_played] += 1
  table[away][:matches_played] += 1

  case score
  when 'win'
    table[home][:wins] += 1
    table[away][:lose] += 1
  when 'lose'
    table[home][:lose] += 1
    table[away][:wins] += 1
  when 'draw'
    table[home][:draw] += 1
    table[away][:draw] += 1
  end
end

The default proc ensures that each sub-hash is present with values initially set to 0.

Instead of a default proc, you could also conditionally assign each initial hash explicitly:

results.each do |home, away, score|
  table[home] ||= { matches_played: 0, wins: 0, draw: 0, lose: 0 }
  table[away] ||= { matches_played: 0, wins: 0, draw: 0, lose: 0 }

  # ...
end

Either of the above results in the following table:

{
  "Team A"=>{:matches_played=>2, :wins=>1, :draw=>1, :lose=>0},
  "Team B"=>{:matches_played=>2, :wins=>1, :draw=>0, :lose=>1},
  "Team C"=>{:matches_played=>2, :wins=>0, :draw=>1, :lose=>1}
}

Upvotes: 0

Related Questions