Route Forward

During my day job, there is rarely any question regarding what I’ll work on next. There may be several stories in a given sprint that haven’t been claimed yet, but my team has experienced some significant turnover over the last 18 months, so I generally just pick the most technically challenging story and move forward with that so that one of the more junior people on the team don’t get stuck with something that they aren’t ready to work yet.

Unfortunately (or fortunately, depending on how you look at things), my choice of what to do during my after hours pursuits are much more open-ended.

In theory, I could do anything. Practically speaking, there is usually a subset of options which are likely to have a higher return on the effort than other options.

In the last year, apart from learning done during work hours, I’ve completed a NodeJS course, done some consulting work, done some work on an email platform (which involved reading a large number of RFC’s in addition to the programming) and completed an Angular course.

If you’d asked me what I was going to work on when I completed my consulting engagement, I would have said that I was going to be working on my email platform for the next several years. Even a few months ago, I would have said that I was quite enjoying my work on the email platform, and that I was going to continue working on it.

That plan was sidelined by a couple of shocks at work, including a story being pulled into a sprint where it looked to me like I was going to need to create a widget from scratch, something that is heavily Angular intensive. Given that I knew very little Angular at the time, that was more than a little alarming, so I decided to take a break from my email platform and get through an Angular class so that I would be prepared the next time that we had something come down the pipe that needed Angular expertise.

Once I finished up with the Angular class, I debated between several different options. I could go back to my email solution. I could go take another class, to either learn a new framework, or even one to just improve my front-end abilities generally. I could go learn more ServiceNow-specific things, or I could spend some time pretending that I was a computer science student again.

I don’t enjoy front-end work as much as I do back-end work, but it was still tempting to sharpen my skills there. Ultimately though, at my current role I don’t do much traditional front-end work with CSS and the like. Additionally, it’s not something that I’m wanting to move into, so it didn’t feel like a great use of my time. I could easily spend a bunch of time getting better with CSS or learning another framework, and then not use what I was learning frequently enough to retain it.

ServiceNow is a great tool, but I’m already familiar with the areas where we spend most of our time at my day job, and I don’t want to spend dozens of hours learning a new module that may or may not get used at my job.

Studying the kinds of algorithms that get studied while pursuing a traditional computer science degree on the other hand feels like the sort of thing that will pay dividends for years to come.

I’m unlikely to ever write a quick sort and use it in production. Frankly, the sorting algorithms built into JavaScript as core parts of the language are more robust and optimized than anything I’m likely to write for production purposes.

The concepts that were used to solve the various problems represented by the algorithms on the other hand are things that I can use over and over again. As I’ve started working through some initial algorithms and data structures, it’s felt a little bit like sitting at the feet of some of the greatest minds of the last several decades, and learning directly from them.

I’m learning a new set of tools, new approaches to solving problems, and blowing the rust off of other tools and concepts that I’ve used in the past, but not recently.

All of which I suppose comes to the point of this post. One of the things that I really love about switching from Accounting to Software Development is the fact that it’s so much easier to make a solid investment in technical skills that make me more valuable than I was before the investment.

When deciding what skill investments to make, it can be tempting to keep chasing shiny new languages or frameworks. There is definitely some benefit to both of those things. Learning Go, which is a synchronous, blocking language help me understand NodeJS (an asynchronous, non-blocking language) at a much deeper level.

Taking an Angular class is useful because using a framework generally allows you to work more quickly than you can without one. Plus it’s something that I use from time to time at my day job.

Paste a certain point though, I think you’re better off working on core competencies. It’s important not to focus all of your time on a dying language or a shrinking platform, but I’m convinced that working on algorithms and data structures–and deepening my understanding of JavaScript/NodeJS is going to be way more valuable to me than learning a second framework or a 5th programming language.

So, when you see more data structure and algorithm type posts in coming weeks, now you know why. And maybe, if you’re just starting out, this post will help you go deeper with regards to your own studies rather than just chasing another shiny new framework or language.

Count Triplets Problem

Every so often, I grab a HackerRank problem in an attempt to learn something new. I recently tried the Count Triplets challenge. As is normal for me with HackerRank problems, the biggest issue was figuring out what the author was after.

Here was my initial code:

function countTriplets(arr, r) {
    let numbers = new Map();

    for(let i=0; i<arr.length; i++) {
        addToHash(numbers, arr, i);  
    }

    let triplets = 0;
    for(let i=0; i<arr.length; i++) {
        let number = arr[i];
        let secondNumber = (number * r);
        let thirdNumber = (number * r * r);

        let secondNumberLocations = numbers.get(secondNumber);
        let thirdNumberLocations = numbers.get(thirdNumber);

        if(secondNumberLocations && thirdNumberLocations) {
            triplets += secondNumberLocations.length * thirdNumberLocations.length;
        }
    }

    return triplets;
}

function addToHash(numbers, arr, index) {
    let locations = numbers.get(arr[index])
        if(!locations) {
            numbers.set(arr[index], [index]);
        } else {
            locations.push(index);
            numbers.set(arr[index], locations);
        }
}

It did exactly what I wanted it to, and returned the number of unique trios of indices that when up by a multiple of ‘r’. Unfortunately, it only passed 8/13 test cases.

As it turned out, they only wanted a match when the numbers in the indices were in order from smallest to largest.

This was my next attempt, which worked, but was too slow to complete one of the tests in the allotted time:

function countTriplets(arr, r) {
    let numbers = new Map();

    let triplets = 0;

    for(let i=0; i<arr.length; i++) {
        const number = arr[i];
        let numberArray = numbers.get(number.toString()); //Number in Map already?

        //Need to get all of the next smallest in sequence already in the Map
        //Lets us count matches and update Map with correct info for number
        let numberFraction = number / r; /*Next smallest in sequence*/
        let numberFractionArray = numbers.get(numberFraction.toString());

        //Do the counting
        if(numberFractionArray) { //potential exists for triplet
            for(let j=0; j<numberFractionArray.length; j++) {
                triplets += numberFractionArray[j].fractionCount;
            }
        }

        //Generate a numberArray object for addition to Map
        let numberArrayObject;
        if(numberFractionArray) {
            numberArrayObject = {
                index: i,
                fractionCount: numberFractionArray.length
            }
        } else {
            numberArrayObject = {
                index: i,
                fractionCount: 0
            }
        }

        //Add current number info to Map so that it can be used by a higher multiple
        if(!numberArray) { //Add for first time
            numbers.set(number.toString(), [numberArrayObject]);
        } else { //Add the new item to the existing array
            numberArray.push(numberArrayObject)
            numbers.set(number.toString(), numberArray);
        }
    }

    return triplets;
}

The above, second version only goes through the initial array one time, but when it finds a number that works as the largest or middle value in the triplet, the execution has to go through the entire array stored on the next number down in the sequence.

This was more or less instinctive for me. I have a tendency to grab hold of any data that comes through under the theory that I’ll probably need it later, but strictly speaking, I don’t need it to comply with the requirements of this particular exercise.

My third version of the code doesn’t try to story any kind of detailed data, instead grabbing summary totals and putting them into the map at each step.

function countTriplets(arr, r) {
    let numbers = new Map();

    let triplets = 0;

    for(let i=0; i<arr.length; i++) {
        const number = arr[i];
        let numberObject = numbers.get(number.toString()); //Number in Map already?

        /* {
            count: x,
            subcount: y
        } */

        //Need to get the value of the next smallest in sequence already in the Map
        //Lets us count matches and update Map with correct info for number
        let numberFraction = number / r; /*Next smallest in sequence*/
        let numberFractionObject = numbers.get(numberFraction.toString());

        //Subcount semi problematic. Need to increment numberObject
        //But only if numberFractionObject exsits
        let additionalSubcount = 0; //Assume 0. Change if numberFractionObject exists

        //Increase Triplets where appropriate
        if(numberFractionObject) {
            triplets += numberFractionObject.subcount;
            additionalSubcount = numberFractionObject.count;
        }

        //Add current number info to Map so that it can be used by a higher multiple
        if(!numberObject) { //Add for first time
            numbers.set(number.toString(), {
                count: 1,
                subcount: additionalSubcount
            });
        } else { //Add the new item to the existing array
            numberObject.count++;
            numberObject.subcount+= additionalSubcount
            numbers.set(number.toString(), numberObject);
        }
    }

    return triplets;
}

My solution is O(n), and the space required is likely to come in around 2n. If there are no duplicates, and no triplets, then it would just be a hash table of size n. If there were no duplicates and everything came back as a triplet, then the size would be 3n because you’d store each item and have two additional data points next to it.

If you have a bunch of duplicates that all come down to the same triplet, then you’d need space of 9 (3 triplets with 3 data points each).

In looking at the editorial on HackerRank and the solution there, the solution requires space of 3n, and they are claiming complexity of O(nlogn), but it looks to me like they are going through the list twice, which would in theory mean that it’s O(2n) which would reduce down to O(n), but my algorithm should be roughly twice as fast.

Table Fields that don’t show in system dictionary (ServiceNow)

I can’t think of a reason why this would or should matter, but it was not at all what I was expecting, so I thought I’d document it in case I need it at some point in the future. Maybe it will prove helpful to someone else.

I recently needed to confirm that iterating through all of the fields from within a server script would really iterate through all of the fields. Much to my surprise, I found out that there are a subset of fields that are on the glide record object (for project tasks), but which don’t show up if I go and look at the table definition in the GUI.

They are:
sys_tags
variable_pool
sys_meta
hierarchical_variables

I just used a for loop to do the iteration. Something like:

for (var field in record) {
   gs.log(field + " : " + record[field]);
}

Finding sys_audit Entries (ServiceNow)

We recently had an issue with something that required running a script to fix some values that had been incorrectly changed. The correct data we needed was in the sys_audit table, but getting to the table to verify that the data really was there turned out to be a miniature adventure all on its own.

The table checkpoints changes to nearly any field an any given form, so it’s huge. My mistake initially was in not thinking about how such a huge table can be so responsive when viewed from a form (in the history/activities section), but so slow when I’m trying to get at the records from a list view.

I’m used to tables in ServiceNow being indexed by one or more date/time fields, but obviously, that’s not how the data is being accessed from the forms.

All indications are that the sys_audit table is indexed by the documentkey field (the sys_id of the record being checkpointed or audited).

In keeping with my theme of also blogging about stuff that I’ll need to lookup at some point in the future, here is the url to get at all of the entries for a given record:

https://[instance].service-now.com/nav_to.do?uri=%2Fsys_audit_list.do%3Fsysparm_view%3D%26sysparm_first_row%3D1%26sysparm_query%3Ddocumentkey%253D[sys_id]%26sysparm_list_mode%3Dgrid%26sysparm_offset%3D

You’ll need to update the [instance] and [sys_id] with the correct fields