in the first and second articles of what has turned into (hopefully) a 3 part series, I went through the creation of my first viewable page of the nba project I’m working on that would show the standings by division. First I had to use SQL and my test data to determine what the standings should be, then design the tests, and then implement the various code in rails so that the content would be displayed properly. Along the way I ran into some unexpected issues in how to find the right data in my tests, rewriting established methods to be more functional, creating a few new ones, and learning some new skills in dealing with arrays (and hashes) in ruby to manipulate my data exactly as I wanted to. All that got me most of the way there, but there’s still a couple more issues to be dealt with:

  • The games behind for each team not in first place has not yet been calculated or displayed on the page
  • The methodology I’ve written is only for one half of the league, it should be relatively simple to run it for both halves after the games behind is done.
  • The code in the view file that creates the HTML is kind of messy, refactoring it into partials would make it easier to deal with, and alter, in the future.

I’m hoping that these 3 issues should be relatively simple to digest and process so that this would be the last part in the articles on the basic standings, so let me get started.

Getting the Game Behind For Teams

In many league standings, you’ll often see a games behind listed for the teams not in first place. It’s usually represented as a whole number (like 3) or a half number (like 2.5). The whole number indicates how many wins by team A and losses by team B are needed for team A to catch up while the half portion indicates either a win by A or a loss by B to break even. In general, a win or loss on its own contributes only 1/2 to the overall calculation of games behind.

I have probably made it sound more complicated than it is, and if so, I am sorry about that, wikipedia unsurprisingly has a detailed analysis of it

There are a variety of ways I can use to calculate games behind but since that wikiepedia page has a formula already, I’ll use that one for my calculations. Assuming the Team A is first place, and that Team X represents the other teams of the division in order, the formula I need to calculate (for each team) is:

  ((teama.wins - teamx.wins) + (teamx.losses - teama.losses))/2

Due to the nature of positioning required to calculated the games behind, this is calculation that can only be done after the record for each team in the division has been obtained and the division ordered from best to worst. This was achieved in the first part of this process, in code this code:

  def standings
    @eastern = Hash.new
    @standingsdate = Game.last.gamedate_display
    Division.east.order(:name).each do |division|
      @eastern[division.name] = []
      division.teams.each do |team|
        @eastern[division.name].push(team.record)
      end
      @eastern[division.name].sort_by!{|hash| hash[:pct]}.reverse!
    end
  end

So after the line that ends .reverse!, each division is sorted in the proper order, by team, and the games behind should be calculated there. In principle, it’s not a complicated process, it just has to be coded carefully. You want to set variables equal to the wins and losses of the first place team and then loop through each of the remaining 4 teams to determine their relative games behind. Games behind will be added to the team.record record hash calculated in the loop above. The first place team will have a games behind setting of while the others will be either an integer or a decimal to .5. All this can be accounted for within the code when written, but first, you gotta write the tests.

I wrote three tests to cover the possible outcomes for the games behind listing:

  1. A first place team should have in their games behind listing.
  2. One test for a team that should have a whole number (ala 3) in their games behind listing.
  3. One test for a team that should have a 1/2 numbers (ala 2 1/2) in their games behind listing.

Those that know coding will be thinking why not just use .5 instead of 1/2 since most languages have a hard time with fractions, and in general, that would make sense, but I need to work out fractions now for this and other future reasons, and I’ve done a little research and believe I have an answer, but anyway, let’s get moving.

Getting the first test to pass is actually pretty simple, it’s a fixed setting on a fixed position (first) in the array that contains all the standings for a given division. Getting the first test to pass is as simple as adding one line to the method above:

  @eastern[division.name][0][:gb] = "--"

Further thought about this line of code and the fact that i’d have to keep accessing the same index of the array made me rewrite this part as below, to make it easier to deal with in the rest of the attribute:

  firstplace = @eastern[division.name][0]
  firstplace[:gb] = "--"

The first test still passes, and the code is now a bit cleaner as the firstplace variable makes accessing needed information easier later on in the method. The first test passes with both lines of code, but that’s the be expected as that test and setting the value was pretty easy. It’s a fixed setting of based purely on the location (first) in the array. Figuring out the games behind for the rest of the array members is, as stated above, more involved and related to the first place information.

My thought process at first was to loop through the rest of the array of the divisional standings and figure out the games behind for each team. While it adds a bit more code to an already complicated method, it felt like it would do the trick. This code was added immediately after the line that set the first place teams game behind to :

firstwins = firstplace[:wins]
firstlosses = firstplace[:losses]
i = 1
while i < @eastern[division.name].length do
  team = @eastern[division.name][i]
  teamwins = team[:wins]
  teamlosses = team[:losses]
  gamesbehind = ((firstwins-teamwins) + (teamlosses - firstlosses))/2.0
  team[:gb] = gamesbehind
  i += 1
end

Note: The weird mixture of text after while i should turn into a <, but for some reason, it isn’t working in the code block. It could be the markdown, it could be the coderay, it could be the combination of two, I don’t know, but I’ll have to fix it later, or perhaps if I convert to TinyMCE/Prism the problem will be solved.

So, simple, this code sets the first place teams wins and losses to existing variables. Using the i variable, and starting at 1, i loop through the rest of the teams in the division, extract their wins and losses and use that to calculate the games behind. The 2.0 at the end means that instead of just integer results, the result will carry out to give me the proper results if half a game is part of the games behind.

We run the tests again, and the whole number test passes, but the fractional test still fails, which is what I expected to happen.

Dealing With Fractions - A New Gem to the Rescue

From my limited experience, fractions are actually a complex pain in coding languages. Dealing with things as decimals is just easier, but in many arenas we want the fraction. For instance, in recipes, you don’t see 0.25 cups of sugar, you’d see 1/4 cup of sugar. I purposely set up my test to look for the fractional response, 7 1/2, instead of the decimal response, 7.5, that the code above yields. I wanted to show the fraction 1/2 where appropriate in the standings as is tradition. Plus, if I ever get around to working on any of my cooking for this blog, I’ll need fractions a bit more. I had hoped that Ruby would have something built-in for fractions because, honestly, it seems like the kind of thing that would exist, but my research indicated that this wasn’t the case. Further research indicated a variety of solutions people built on their own but than I found it. Unsurprisingly, there is a gem set up to help coders convert their numbers into fractions. I played a bit with the gem following the instructions, and quickly came upon this code:

  Fractional.new(7.5, to_human: true).to_s

When you plug this into your ruby terminal it returns 7 1/2 as a string exactly as you wish. So it seems that I’ve found the perfect solution. I install the gem and alter the view page to display the games behind as in the example above and run my test again. The last test is passing, the 7.5 is being converted to 7 1/2, however, the ‘easy’ test is failling. The first place test that just required setting games behind to without further calculations is being converted to fractional, so I have to deal with that complication.

Though not ideal, I knew since I was carrying the index (the place indicator) through my loop, I could use that value on a conditional check to decide whether or not to process the :gb entry through Fractional. To make it slightly more clean I used a ruby ternary. A ternary is just a way to write a simple if else in less code. So the ternary I wrote ended up looking like this:

  index == 0 ? team_info[:gb] : Fractional.new(team_info[:gb], to_human: true).to_s

This tweak allowed all the tests to pass, so there it was, the standings, for one conference, had been passed, but now I had to include the other conference and maybe clean up the code a bit for ease of use.

Expanding the Standings to Encompass Both Conferences

I couldn’t say why I built this method, and testing scheme, to cover only the eastern conference. Obviously the western would need to be included as well, but for some reason I got it in my head that doing one conference at a time just made more sense. I’m not sure it really does, and, ideally, incorporating the western standings won’t be too hard. It should be just a matter of extending out the hash idea so that there is a standings hash passed that has two keys, one for eastern and one for western, and then the rest should play out the same way. Of course, tests need to be written to make sure, so that is the first test.

One of the things I want to test is to make sure that the eastern conference does display before the western conference, but right now my code is a whole lot of divs, and we’ve established earlier that capybara doesn’t like to search through divs without some context. I think a data target like used for divisions might be better for making sure I can find the position of the first and second one.

For those thinking, why doesn’t he use xpath I’m aware of the xpath option but not fully versed in it and at this point it seems like something I should research before delving in to

For now I just called it data-marker so I could look within("div#standings div[data-marker='conference']:nth-child(1)") and determine the eastern conference showed up there. This test failed at first of course because it wasn’t in my view, but I created a div with the id of standings and then added a div underneath with the data-marker and then hard coded Eastern Conference into the text. To really get this to completion I’m going to have to refactor the view. On a hunch, I rewrote the within statement to read within("div#standings div[data-marker]:nth-child(1)") , and the test still passed. This wasn’t a certainty but I hope, so now I can change data-marker to data-conference because that makes more sense.

The next step obviously is to reinforce the test that the text western conference shows up as the second child, so that test is written, and obviously fails, but it’s not an easy fix. Now I have to rewrite the controller method (which might have to move into the model at some point) so that it handles both conferences as one set, so now we have to do that.

The original method (above) only goes through the eastern conference, so, in theory, I just have to loop through the western conference as well. Ideally though, I think you’d want to pass the conferences together as one instance variable so @eastern above would be changed to @standings and its final appearance slightly altered to contain both conferences. The complexity of this will come when rewriting the display page which I might turn into distinct pieces to keep it all clean and easy to follow.

So I could hard code the names of the two conferences into the controller method, but what if, for some reason, in the future, the nba decides to change conference names (it could happen, other leagues have changed names like this), so using my new found knowledge of Active Record, I can use our favorite pluck and the SQL concept of distinct to pull the conference names (only once) from the division table.

  Division.distinct.pluck(:conference).sort

The sort at the end guarantees that the eastern comes before the western and that’s important because of how keys are accessed in hashes. Eastern will be the first key so the data is then pulled. So, now that I had the two conferences, I had to rewrite my method above just a bit as the looping was going to be one step deeper and the final product would cover all divisions, not just the eastern one. I won’t rewrite the whole thing, but suffice to say that it probably seems a bit messy to more experienced coders but it does the job I need it to do.

So, once I got the method written so that both conferences were contained in my data, I ran my tests again, and unsurprisingly, they all failed, which was to be expected, for a couple of reasons. For the first primary reason, @eastern was re-branded to @standings, so all those references would have to change, but more importantly with the depth of the resulting hash increased to account for both conferences, that needed to be accounted for on the display page as well.

Rewriting the View and Factoring into Partials

So, I needed to expand my view code to encompass this new hash, but at the same time, my view code was already pretty crowded. Though I’m not ready to move onto a presentation layer concept, the code for this definitely needs some cleaning up. There are some natural break points if you look at it. You loop through each conference three times to get the divisions therein, and then you loop through each division five times to access each teams information. It would be ideal, if you could somehow display it all on the same page, but isolate the code responsible for each of these loops. And like most things in Rails, if it seems like there’s something that just makes sense, there’s a built-in way to do it. For this situation we’re going to work with partials.

The basic idea is that partials allow you to extract certain parts of the layout code that might be a bit complex into their own individual files you can call later. Not only does this keep things clean and compartmentalized, it also allows you to use the same piece of partial layout code on multiple pages. A common partial is the _form (the _ is actually required with in the conventions of rails) partial. The creation and editing, in the basic set up, tends to be the same display even with different functionality. Using the form partial means you only have to write it once and you can use it twice (or more). In this, partials will come in very handy for my standings.

One key thing I’m going to have to keep very good track of is my local variables. As there are multiple loops, I believe I will write multiple partials. There will be a partial called conference which calls a partial called division and that will refer to a partial called team. Each of those partials need data transferred to them. Rails does allow for this using the local variables, I just will have to make sure that I don’t lose any of the data along the way.

Since at this point my blog has issues displaying HTML, I will explain what I’m going to do in a way that hopefully will make sense to all levels of reader.

I’ll start with the @standings on my primary page, looping through you obtain the key (conference name) and value (divisions hash). I will pass both values to the conference partial. Within the conference partial I will put in the basic conference information but then I’m looping through the division hash. In that loops, I will pass both the division name and the array with the teams to the division partial. Within that partial, one more loop, through the teams array, will be passed to, obviously, the team partial. Ideally the layout will look exactly the same but be easier to edit in the future. (Also, in terms of a conference standings instead of standings by division, I could reuse the same partials in different combinations and sequences and not have to rewrite them at all.)

To do this properly, I not only have to write some more tests, that will fail at first, I will have to build this up in a stepwise fashion, so all my passing tests will fail, and then slowly pass again as they are built up.

So the process will be to comment out all tests and then uncomment them step by step as well. At first that means that only two tests will be run, to determine if the conference displays properly. This is a pretty simple one, so the partial is written and the standings page altered to start by rendering that conference partial and the tests regarding the display of the conference name pass.

Now, I want to write some tests about divisional positioning that I haven’t written before, because the divisions are rendered next. Using the data block concept again, I used the data-division idea to write tests to determine that each division came out in the right order in the right conference. Then it was just a matter of extracting the right code from the original standings to write a partial for displaying a division, which though it took a little longer, it got done as well, so that meant we were on to the final step, testing the team placement and rendering it properly.

Creating the team partial was pretty simple since there’s no new information or looping required in this context. I did have to remember to pass the name of the division to the partial because it is used in the laying out (and identifying for tests) of each team. I’ll admit it took a little longer than it should have, as I didn’t just copy the coding for the teams but tried to refine it a bit. I ran in to targeting issues in the tests again, so I reverted to the code that was working previously and added a few tests. i uncommented all the tests that were still commented out and got them all to pass, and then we were done.

So, that’s it, for now. The simple process of getting the standings displayed is finally done. The layout is still not perfect and needs some work, but the process of getting the data and make it show up properly (and testing it) has been completed. What’s left is purely decorative. Refining the layout to make it look cleaner is one thing that needs to be done and one other small thing that is testable but also a display issue is that a team with no losses should display as 1.000 but displays as 1.0, and a team with no wins, should display as .000 but display as 0.0, edge cases that I hadn’t thought about until I saw how certain teams were displayed, but those are issues for another day, I have the first layout display of the nba application done to a usable readable form and that was the goal, and now it’s on to the next.