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.

Friday, 14 July 2017

d3.json and Data URLs

I've been playing with d3 quite a bit lately and I know there's a load of stuff I need to learn more about but I thought I'd share this little trick associated with d3.json.

I've had a couple of occasions where I needed to pre-process the json sent to a d3 script. Each time I've been stumped by ds.json requiring an URL, I'm not going to process the data and then send it back to the server in order to get it again, so I started thinking about Data URLs and base64 encoding (as is my wont) and this sorts it out a treat:

let url = "data:application/json;charset=utf-8;base64,";
url += btoa(JSON.stringify(processedObject));
d3.html(url, function(error, graph) {
    if (error) throw error;
    // do cool things with the data...
});

Anyway, I hope it helps someone (and yes, I do know you don’t need to do it, it’s just nice not to change the original scripts too much!).

Tuesday, 13 June 2017

JavaScript - shorthand if statement (sans else)

I'm all in favour of shorthand statements and I often visit this Sitepoint page. I'm also keen on ES6 destructuring as I seem to spend a lot of time thinking about the shape and patterns of data anyway.

One shorthand I've always like is the if using &&:

Given these constants:

const thing  = "fred";

const helloWorld = () => {
    console.log("hello");
    console.log("world");
};

We can do this:

thing === "fred" ? helloWorld() : null;  // hello\nworld

thing === "Fred" ? helloWorld() : null;  // nowt

We can also do this:

thing === "fred" && helloWorld(); // hello\nworld

thing === "Fred" && helloWorld(); // nowt

But, even better, we can do this:

thing === "fred" && (() => {
    console.log("hello");
    console.log("world");
})();  // hello\nworld

thing === "Fred" && (() => {
    console.log("hello");
    console.log("world");
})();  // nowt

How cool is that? JSFiddle here.

Wednesday, 26 April 2017

For loop -vs- Map

I got asked last week about getting the index of an object in an array which had the earliest date, so that this array:

let objArr = [
    {
        "date": "2011-08-12T20:17:46.384Z"
    }, {
        "date": "2012-08-12T20:17:46.384Z"
    }, {
        "date": "2013-08-12T20:17:46.384Z"
    }, {
        "date": "2014-08-12T20:17:46.384Z"
    }, {
        "date": "2010-08-12T20:17:46.384Z"
    }, {
        "date": "2009-08-12T20:17:46.384Z"
    }
];

Produces 5 as that element has the earliest day.

I pondered using map but then decided to go all old school and used a for loop like this:

let boringParse = i => parseInt(moment.utc(i.date).format("x"), 10);
let forLoopInt = objA => {
    let min, i, objALength = objA.length;
    for (let y = 0; y < objALength; y++) {
        let now = boringParse(objA[y])
        if (!min || now < min) {
            min = now, i = y;
        }
    }
    return i;
};

Once I got back to my desk I decided to talk it over with James and came up with this:

let boringParse = i => parseInt(moment.utc(i.date).format("x"), 10);
let mapOne = objA => {
    let datesArr = objA.map(boringParse);
    return datesArr.indexOf(Math.min(...datesArr));
};

Turns out that the for loop is the faster, but only just!

Saturday, 22 April 2017

WebSQL bulk insert

I've been playing with WebSQL of late (after all, why wouldn't you play with a deprecated standard?). And I'm loving it for inserting data via Papa Parse, the thing is, it was proving to be really, really slow... right up until I clocked that I was doing it wrong!

The thing is, I was iterating over my data and firing a transaction with an SQL payload for each row of the CSV file I was parsing where I should've been firing a transaction and then iterating over my data and firing off multiple SQL payloads within the single transaction.

This was my first effort:

for (let record of results.data) {
    mydb.transaction(
        t => {
            t.executeSql(`
                INSERT INTO
                  accounts (
                    Date,
                    Company,
                    Account,
                    Amount
                  )
                VALUES (
                  ?,
                  ?,
                  ?,
                  ?
                )`,
                [
                    record.Date,
                    record.Company,
                    record.Account,
                    record.Amount
                ],
                t => {
                    let query = "SELECT COUNT(*) AS total FROM accounts";
                    t.executeSql(
                        query,
                        null,
                        (t, result) => {
                            if (~~result.rows[0].total === tableMetaData.total) {
                                createTable();
                            }
                        },
                        null
                    );
                },
                null
            );
        }
    );
}

Pretty, eh? But it took silly amounts of time to execute and I was planning on inserting 250,000 records. It simply wasn't practical to do it that way as it took something like 20 seconds to insert 1000 rows:

RowsTime
100.218
1001.711
100021.111
500090.703

Changing the code to this:

mydb.transaction(t => {
    for (let record of results.data) {
            t.executeSql(`
                INSERT INTO
                  accounts (
                    Date,
                    Company,
                    Account,
                    Amount
                  )
                VALUES (
                  ?,
                  ?,
                  ?,
                  ?
                )`,
                [
                    record.Date,
                    record.Company,
                    record.Account,
                    record.Amount
                ],
                null,
                null
            );
        }
    },
    error => {
        console.log("error", error);
    },
    () => {
        createTable();
    }
);
Produced these results:
RowsTime
100.081
1000.058
10000.083
50000.223

Not only quicker but I've also halved the number of queries as I don't need to check that all the transactions have completed after each insert, sweet eh? After figuring this out I came across a number of posts where the same thing had been discovered by others. Including RICAL and Gavin Pickin. Just a shame it's deprecated.

Thursday, 13 April 2017

JavaScript and checking for 0

A friend and colleague who's new to the joys of JavaScript asked about checking for a value which is zero and I automatically suggested the old double tilde trick.

I worked up a JSFiddle to illustrate the concept:

let myArray = [
    0, "0", '0', [0],["0"],['0'], 
    1, "1", '1', [1],["1"],['1']
];

for (let value of myArray) {
    console.log(value, typeof value)
    console.info(
        "~~" + value + " === 0:",
        ~~value === 0);
}

But then I got to thinking about what else the value might be, like an object, string or an array of strings: {"name":"Fred"}, "Fred", 'Fred', [{"name":"Fred"},{"name":"Fred"}], ["Fred"], ['Fred'].

These, oddly, also equated to true when compared to 0. So I started thinking about checking the type and doing something interesting (complicated) but then I remembered the toString() method and all the issues I've had in the past with seeing [Object, Object] in pages I've been working on.

Perhaps I could use that and reverse the solution?

So instead of checking for 0 as a number I'd instead check for it as a string. This, then, is a solution that seems to work quite well:

let myArray = [
    0, "0", '0', [0], ["0"], ['0'], 
    1, "1", '1', [1], ["1"], ['1'], 
    {"name":"Fred"}, "Fred", 'Fred', [{"name":"Fred"},{"name":"Fred"}], ["Fred"], ['Fred']
];

for (let value of myArray) {
    console.log(value, typeof value)
    console.info(
        value + ".toString() === '0':", 
        value.toString() === '0');
}

Hope that helps someone, it's something I'll keep in mind in future!

Monday, 3 April 2017

Avoiding Uncaught TypeError: Cannot read property '*' of undefined in JavaScript

I've lost count of the number of times I've run into issues diving into a deep JSON structure only to be confronted with the error TypeError: Cannot read property '*' of undefined. It's one of my major bugbears, and I've spent so many hours staring at it that I've finally come up with a way of avoiding it, the thing is, it uses eval...

I'm pretty sure that this use isn't overly evil but I'm not 100% sure TBH

Let me illustrate my use case and solution. Given this JavaScript object:

let obj = {
    "one": 1,
    "two": 2,
    "three": {
        "threeOne": 31,
        "threeTwo": 32
    },
    "four": 4
};

We can interrogate it in all sorts of ways:

console.log("obj:", obj) // Object {one: 1, two: 2, three: Object, four: 4}
console.log("obj.three.threeOne === 31:", obj.three.threeOne === 31); // true
console.log("obj.three.threeTwo === 31:", obj.three.threeTwo === 31); // false
console.log("obj.three.threeThree === 31:", obj.three.threeThree === 33); // false
console.log("obj.five === 31:", obj.five === 31); // false
console.log("obj.five:", obj.five); // undefined

But if we start digging a little deeper we run into trouble:

console.log("obj.five.fiveOne === 51:", obj.five.fiveOne === 51); 
// EXCEPTION: Uncaught TypeError: Cannot read property 'fiveOne' of undefined
console.log("obj.five.fiveOne:", obj.five.fiveOne); 
// EXCEPTION: Uncaught TypeError: Cannot read property 'fiveOne' of undefined

Except that's not 100% accurate as the browser throws its hands up after the first error and the second won't be shown as all bets are off!

It's not the fact that we're asking about an undefined thing but that we're asking about a property of an undefined thing. Should this then mean that we should check to see if the parent element has such a property before interrogating it, like this?

if(obj.hasOwnProperty("five") && obj.five.hasOwnProperty("fiveOne") && obj.five.fiveOne === 51){
    console.log("obj.five.fiveOne === 51:", true); 
}else{
    console.log("obj.five.fiveOne === 51:", false);
} // false
if(obj.hasOwnProperty("five") && obj.five.hasOwnProperty("fiveOne") ){
    console.log("obj.five.fiveOne:", obj.five.fiveOne);
}else{
    console.log("obj.five.fiveOne:", undefined);
} // undefined

This works though, depending upon the depth of the object this can get really ugly really fast, perhaps we should take advantage of the errors and do something with them. This we can do using a try...catch::

try {
    console.log("obj.five.fiveOne === 51:", obj.five.fiveOne === 51);
} catch (ex) {
    console.log("obj.five.fiveOne === 51:", false);
} // false
try {
    console.log("obj.five.fiveOne:", obj.five.fiveOne);
} catch (ex) {
    console.log("obj.five.fiveOne:", undefined);
} // undefined

This works, and it works well, but it is almost as ugly! To tidy it up I started writing a simple function but soon ran into a problem where, if I passed something which didn't have the property then the call to the function failed and caused a similar error. Then I clocked I could use a string and the eval function within a try...catch to do the work, I also added in some default checking of the value for my particular use case, but this is not necessary really:

let testObject = (objPath, val) => {
    val = typeof val !== 'undefined' ? val : "Completed";
    var result = false;
    try {
        result = (eval(objPath) === val) ? true : false;
    } catch (e) {}
    return result;
};

The above testing can then be replaced with this:

console.log("testObject('obj.three.threeOne', 31):", testObject('obj.three.threeOne', 31)); // true
console.log("testObject('obj.three.threeTwo', 31):", testObject('obj.three.threeTwo', 31)); // false
console.log("testObject('obj.three.threeThree', 31):", testObject('obj.three.threeThree', 31)); // false
console.log("testObject('obj.five', 31):", testObject('obj.five', 31)); // false
console.log("testObject('obj.five.fiveOne', 51):", testObject('obj.five.fiveOne', 51)); // false
console.log("testObject('obj.five.fiveOne'):", testObject('obj.five.fiveOne')); // false

This is already helping me; I hope you might find it useful as well. Here's a working JSFiddle with the code. I've made use of the marvellous console-log-div to output the results to a div. Otherwise, you'd need to open the DevTools on the JSFiddle to see the results. Hope it helps someone, and if you have any comments, please do chip in below.