Wednesday, 16 August 2017

Handicap curves from Archers Mate

I've been doing a little work on investigating archery handicaps for Witchford Archers, and it's been a fascinating journey into statistics. The Club Secretary, God-bless-him, has passed me all sorts of facts and figures and I've been looking at the code on the Archer's Mate website. We'd also attended a meeting of the CAA and discovered that working out your Handicap was a useful thing to do (especially if you enter competitions - for me, less so).

On the site, there is a simple form which allows a user to enter the round they are shooting, the type of bow they are using, the type of target they are shooting at, their gender and their score.

I was only really interested in the Portsmouth handicaps, so I was a wee bit of a pain and hit the server with lots and lots of requests (I did it during odd hours, so hopefully it didn't impact things too much).

Anyway, I discovered that the handicaps didn't differ between men and women but that the curve varied between the different bows and targets. I guess the main take away was that the user shooting a compound at a full target started off the same as someone shooting a recurve bow at the same target, as their score increased though, they began to follow the handicaps of those shooting a compound bow at a triple target.

Hopefully, the graph above illustrates what I mean. I'm not going to hit the server anymore, but I think I'll do the same to the handicaps published by Archery GB and see if there's any correlation.

There did seem to be a difference in the classifications between men and women though... not sure what that's all about.

Tuesday, 8 August 2017

Complex object creation in JavaScript

In JavaScript you can get a little spoilt with the initialisation of variables. For example I might have something like this:

const start = {
    "x": 20,
    "y": 20
}, end = {
    "x": 40,
    "y": 60
}, width = end.x - start.x, height = end.y - start.y;

Where width and height rely on the previously set variables.

Today, at work, I was asked if there was a way of doing something similar regarding initializing an object. This pseudo code illustrates what I mean:

const psuedoObj = {
    "start": [20, 20],
    "end": [40, 60],
    "width": this.end[0] - this.start[0],
    "height": this.end[1] - this.start[1],
};

console.log(psuedoObj);

The thing is, that doesn't work.

So we got to looking and found this answer on Stack Overflow. To get around the issue we could then do this:

const psuedoObj = {
    "start": [20, 20],
    "end": [40, 60],
    "init": function(){
        this.x = this.start[0];
        this.y = this.start[1];
        this.width = this.end[0] - this.start[0],
        this.height = this.end[1] - this.start[1],
        delete this.init
        return this;
    }
}.init();

console.log(psuedoObj);

Nice ehh? And we even tidy up after ourselves!

Wednesday, 2 August 2017

Some thoughts on pagination

I've written an adaptation of DataTables Pagination for use with MDB before but I've never really had to think about pagination much until, that is, a recent project at work came up. I was laid-up at home with a cold, so I used my time to think about it in some depth. For my use case, I wanted a set of buttons starting with a First and Previous button and ending with Next and Last buttons. I spent some time looking around, and it seemed as though the best arrangement of buttons for individual pages had an odd number.

This, then, was my desired output:

1 2 3 4 5

With the currently active page disabled for it to be distinguishable (and immune to clicking - I'm not sure why that's as important as it is to me, but it always pleases me if I'm unable to link back to the page I'm on from the page I'm on). I guessed that I'd also need to disable the First and Previous buttons when the first page was active and the Next and Last when the last page was active.

This then meant that the as far as possible the active number should be in the centre of the range of numbers. In the example above then when three was active I'd be happy. However, should four be the active page then the range would start at two and end at six. When one is the active page then it is not in the middle of the range; so we don't need to show numbers which are impossible to display (we really can't get to the page with the number minus one).

If we had a range of seven pages of data to display, this is what we should see while paging through the data:

Active: one

1 2 3 4 5

Active: two

1 2 3 4 5

Active: three

1 2 3 4 5

Active: four

2 3 4 5 6

Active: five

3 4 5 6 7

Active: six

3 4 5 6 7

Active: seven

3 4 5 6 7

This, then presented me with some considerable confusion, not least because I had trouble thinking clearly - but after far too many hours of pondering I can up with this spreadsheet and started reasoning about the logic behind calculating what I needed to happen.

After playing with the sheet, I clocked the algorithm required needed to check a number of different figures in order to decide where to start and end the range:

=IF(LTE(A3, CEILING(DIVIDE(D2, 2))),
    1,
    IF(GT(ADD(A3, FLOOR(DIVIDE(D2, 2))), A14),
        ADD(MINUS(A14, D2), 1),
        MINUS(A3, FLOOR(DIVIDE(D2,2)))))

Bonkers ehh?

I wrote the first line and clocked what was going on and decided to cheat after that, so I wrote a JSFiddle that generated the correct Google Sheet code for me, which I could then use to create the finished product. The final bit of code is rather simple, and this is it:

const generateCell = (text, active) => {
    const cell = document.createElement("td");
    cell.textContent = text;
    active && cell.classList.add("active");
    return cell;
}
const generateRow = (f, a, b) => {
    const row = document.createElement("tr");
    row.append(generateCell("", a === 1));
    row.append(generateCell("", a === 1));
    for(let i = f; i < (f + b); i++){
        row.append(generateCell(i, i === a));
    }
    row.append(generateCell("", a === total));
    row.append(generateCell("", a === total));
    return row;
};
const total = 12;
const table = document.getElementById("body");
const buttons = 5;
for(let active = 1; active <= total; active++){
    let first = (active <= Math.ceil(buttons / 2))
        ? 1
        : (active + Math.floor(buttons / 2) > total)
            ? (total - buttons) + 1
            : active - Math.floor(buttons / 2);
    table.append(generateRow(first, active, buttons));
}

I'm sort of ashamed that it took quite as long as it did, but I’m rather pleased with the result.