Divisible Sum Pairs (HackerRank Problem)

Hacker Rank problems can be fun, and I often find myself learning something interesting. I recently undertook the Divisible Sum Pairs problem.

In short, given an array of integers ar, and an integer k, find how many pairs of integers add up to a multiple of k.

The trivial solution is just a double for-loop that has an O(n^2) run-time.

I often find that by the time I’m done reading through the Hacker Rank problems and go start working through the actual code to solve the problem, that I’ve let a key detail slip. Such was the case this time, and my original solution found the total of the pairs of integers that added up to k rather than a multiple of k.

Because of that, my initial implementation used a hash table, and I decided to try and expand out my initial hash table solution to deal with multiples. Here is my solution:

function divisibleSumPairs(n, k, ar) {
     let myMap = new Map();
     let pairs = 0;
     let biggestValue = ar[0];

     for(let i=0;i<ar.length;i++) {
          let maxLookingFor = ar[i] + biggestValue;
          let iterations = Math.ceil(maxLookingFor/k);
               for(let j=1; j<=iterations;j++) {
                    let lookingFor = k*j-ar[i];
                    const mapValue = myMap.get(lookingFor)
                    if(mapValue) {
                         pairs += mapValue;
                    }
               }

               if(ar[i] > biggestValue) {
                    biggestValue = ar[i];
               }

               const mapValue = myMap.get(ar[i]);

               if(mapValue) {
                    myMap.set(ar[i], mapValue + 1);
               } else {
                    myMap.set(ar[i], 1);
               }
          }

     return pairs;
}

Here is the least efficient:

function divisibleSumPairs(n, k, ar) {
     let pairs = 0;
     for(let i=0;i<ar.length;i++) {
          for(let j=i+1;j<ar.length;j++) {
               let sum = ar[i] + ar[j];
                    if(!(sum%k)) { //!sum%k does !sum then %k's the !sum
                         pairs++;
                    }
          }
     }
     return pairs;
}

Here’s the most efficient solution:

function divisibleSumPairs(n, k, ar) {
     let numbers = [];
     for(let i=0;i<k;i++) {          numbers.push([]);     }
     for(let i=0;i<ar.length;i++) {          let remainder = ar[i]%k;          numbers[remainder].push(ar[i]);     }
     let count;
      //populate the zero row     let zeroQuantity = numbers[0].length;
     count = (zeroQuantity * (zeroQuantity -1)) / 2;
     let j = k -1;
      //populate the remaining rows
     for(let i=1; i<=j;i++) {
          if(i == j) {
               //remainders add to k
               let middleRowQuantity = numbers[i].length;
               count += (middleRowQuantity * (middleRowQuantity - 1)) / 2;
          } else {
               //remainder is exactly half k 
               count += numbers[i].length * numbers[j].length;
          }
          j--;
     }
     return count;
}

As k gets smaller, the range of numbers gets bigger, and the length of the array ar gets smaller, the ‘worst’ alogorithm will tend to do better compared to my solution. As k gets larger, the range of numbers gets smaller, and the size of the array gets larger, then my solution will tend to perform better.
 

The most efficient solution is obviously better than either of the other two. If we were talking a problem where the range of numbers in the array was very small, or the range was very small until the last step, k was very large, and we needed the pairs returned, then it’s possible that the generation of the pairs at the end of the efficient solution could take longer than my solution, but that’s a pretty extreme, narrow kind of edge case.

If I ever run into a similar problem in the wild, I’ll now know how to approach it thanks to the commenters in the discussion section on Hacker Rank.

ServiceNow – the “New” UI Button & URL’s

I recently had an issue where I needed to grant a specific role the ability to create a new record.

They needed to be able to click the ‘New’ button, enter in the relevant information on the record, and then save the record.

It turned out that there were two issues preventing that role from being able to create a new record.

The first was in the List Control under List v3:

I’m including a screenshot of the list control screen, and it’s worth pointing out that there are three different spots there where you can stop someone from seeing the ‘New’ button. If you skim over that screen and miss one of the three like I initially did, you’re likely to waste a lot of time banging your head against a wall.

There was also an ACL issue that was in play with this story, but the  more interesting bit was some troubleshooting advice that I got in the SN Devs slack channel.

I explained my issue and that I’d checked everywhere I could think to check, and asked where else the ability to create new records might be being locked down. Someone there (I afraid I don’t remember who), told me to go to

https://myInstance.service-now.com/nav_to.do?uri=%2Fsys_user_group.do

They suggested that I do that because it pulled the ‘New’ button out of the picture and confirmed whether or not there was an ACL issue still giving me grief.

In the above url, sys_user_group.do is telling servicenow to go to the create record view for the sys_user_group table.

sys_user_group_list.do would take me to the list of groups.

There isn’t anything revolutionary there, but starting out in ServiceNow, one of the things that slowed me down was not understanding some of the small quality of life type things built into the platform.

That includes stuff like not knowing that I could right click in the form view and copy the sys_id of the record into my clipboard that way.

I also didn’t understand initially that I could get the table name right out of the url.

I’ve known how to get to a list of records in a table by typing the table name.list into the application navigator, but now I’ve got one more tool in my toolbox, for which I’m very grateful.

ServiceNow API Failure Triggering Catch but not Passing Message

I come across plenty of interesting, blog-worthy bits and pieces while working my day job, but it wouldn’t be right to stop and write up a blog post on the company dime, so I tend to just jot down an (often cryptic) note when I come across something and then proceed on with whatever story or project brought the tidbit to my attention.

Then, when I have a bunch of items that need blogged about, I’ll take an hour or two after work or on a weekend, write up a bunch of blog posts, and then schedule them to go live at a rate of one per week.

That means that the stuff hitting my blog is unfortunately generally weeks or even months old, which is unfortunate because it’s not as representative of my current skill level, but that’s a trade-off I have to make in order to avoid missing large chunks of time where I’m supposed to be posting blog entries, but am tied up on some consulting project that is eating up all of my non-work time.

Currently I’m doing some consulting after hours which has meant blogging time has been non-existent for several months now, and some of my cryptic entries are even older and therefore more cryptic than normal.

One of my notes is something to the effect that when an outbound API call from ServiceNow fails, it will trigger a catch (from a try/catch), which means that you can log that it failed, but that whatever is failing is lower-level than the script calling the API, and that something doesn’t pass the normal error message, which means that it’s a lot harder to debug that particular issue than it otherwise would be.

I don’t remember the exact circumstance around this note though, so I haven’t done my normal testing before writing this blog post. All of which means that you’ll want to take this particular find with an extra grain or two of salt.

ServiceNow ACL to Create a Record

I very much understand the necessity of ACL’s in ServiceNow, but it’s taken a couple of different stories (we work using SCRUM at my day job) for some of the intricacies of ACL’s to sink in for me. A lot of the stuff that was tripping me up early on was very basic, but a couple of things that I’ve come across feel more like idiosyncrasies to ServiceNow that will cause me grief again at some point, so those are things that I need to document for future me’s benefit.

A case in point: it turns out that in order to create a new record in a table, you not only have to have the ‘create’ rights, you also have to have ‘read’ rights on the table. I would have expected that just having create rights would do the trick. It’s possible that I misunderstood what was going on though, so I’m documenting my experience here and then I’ll test that out the next time I have a story that involves adding new ACL’s into a ServiceNow instance.

“Hidden” scripts in ServiceNow

I had an interesting experience recently. I was working on an ACL, and my initial approach involved adding a script to the ACL. As I continued working on the story that required the ACL, I unchecked the ‘advanced’ checkbox on that ACL, and then tested the user’s access again.

Much to my surprise, I realized that the script was still running even though the ‘advanced’ checkbox was no longer checked.

As nearly as I can tell, the checkbox hides the script from the user, but it’s still running even though the script is hidden.

My expectation was that unchecking the box disabled (but didn’t delete) the script, but that appears to be wrong, so make sure if you want a script not to run that you clear out the editor rather than just unchecking the box.

Determining if Something is an Array

I had an issue recently where I thought I was dealing with one kind of variable, but in fact was dealing with a different kind of variable.

During the course of debugging that particular block of code I realized that you can’t use typeof to determine whether or not something is an array.

Typeof returns ‘object’ for both an ‘object’:

const myObject = {

     “name”: “object 1”,

     “number”: 1

}

and an array:

let myArray = [1, 2, 3, 4]

As it turns out, there are at least two easy ways to determine whether or not something is an array.

Array.isArray() can be passed a variable and will return true if that variable is an array. Fortunately it is on old enough bit of functionality that it works with ServiceNow’s Rhino engine.

Additionally, ServiceNow’s gs.log() method will log out an array, but will log out ‘object’ for an object, so you can often tell what you’re dealing with just based on what you’re getting logged out as your script runs.

Populating a Duration Field

I recently had a use-case where I needed to populate a custom date/time field with time that had passed since a group of agile stories had been created.

Here is how I did that:

var currentTimeStamp = new GlideDateTime(); //Create current timestamp
currentTimeStamp = currentTimeStamp.getDisplayValue(); //Set it to a string so dateDiff can us it

var gr = new GlideRecord(‘rm_story’); //Get active, unassigned stories
gr.addQuery(‘active’, true);
gr.addQuery(‘sprint’, ”);
gr.addQuery(‘assigned_to’, ”);
gr.query();

while(gr.next()) { //Loop through the stories
     var startTimeStamp = gr.getDisplayValue(‘sys_created_on’);
     var dur = gs.dateDiff(startTimeStamp, currentTimeStamp, false);
     var durObject = new GlideDuration(dur);
     gr.u_time_since_creation = durObject; //Set value in custom field
     gr.update();
}

 

 

Throwing an Error Message From Inside of a Business Rule

I recently came across a situation where I needed to be able to log a user-visible message to the screen from a business rule.

Fortunately, this is possible:

var msg=’What you are trying to do is not allowed’;

gs.addErrorMessage(‘<script>var t=setTimeout(function(){var test = confirm(“‘ + msg + ‘”);if(!test){return false;}},500);</script>’ + msg);

Hat tip to Anurag Tripathi over on the community forums for the solution: