Join the #BuildWithBuildAgent Challenge! Get recognized, earn exclusive swag, and inspire the ServiceNow Community with what you can build using Build Agent.  Join the Challenge.

SlightlyLoony
Tera Contributor

Not long ago, I helped one of our customer's developers with some JavaScript issues that were making her want to scream. Actually, she did scream — right in my ear as she was describing her frustration. It was mainly two relatively little things that were bugging her. First, she had a list of software object instances in an array, and she wanted to sort them by price. She knew she could write a custom sort function to do this, but she really didn't want to do that — she wanted an easier way. Second, when she computed the extended price of a purchase, she got a very funny looking result.

"Help!" said she. Loudly.

Here's a simplified version of what she had when she called me:


var Software = function(name, price) {
this.name = name;
this.price = price;
};
Software.prototype.toString = function() {return this.name;};


var s1 = new Software("X", 400);
var s2 = new Software("A", 359.99);
var s3 = new Software("M", 19.98);

var s = [s1, s2, s3];

s.sort();
for (var i = 0; i < s.length; i++)
gs.log(s.name + ' ' + s.price);
gs.log(10 * s1.price + 5 * s2.price + s3.price);

When she ran her code, she got something like this:

A 359.99
M 19.98
X 400
5819.929999999999

There were two big problems with this from her perspective: the list was sorted alphabetically by name whereas she wanted it sorted in order of price, and the value at the bottom was screwy looking. Plus the way the prices were formatted in the list were hard to read, and the code that does the math seemed hard to read (with all those .price pieces in there).

The reason her list sorted in alphabetical order was because she supplied a toString() method (which JavaScript will use to convert her objects to a string), and the default behavior of the Array.sort() method is to sort alphabetically. The "normal" way to solve this problem would be to write a custom sort function — but that's exactly what my frustrated lady didn't want to do (she finds them overly complex and hard for novices to understand). Well, ok. We can do what she wants by changing the .toString() method to return a string that sorts in the order she wants. You can see that in our tweaked code below:

var Software = function(name, price) {
this.name = name;
this.price = price;
};
Software.prototype.valueOf = function() {return this.price;};
Software.prototype.toString = function() {return rpad(this.price.toFixed(2), '0', 10);};

function rpad(val, pad, num) {
while (val.length < num)
val = pad + val;
return val;
}

var s1 = new Software("X", 400);
var s2 = new Software("A", 359.99);
var s3 = new Software("M", 19.98);

var s = [s1, s2, s3];

s.sort();
for (var i = 0; i < s.length; i++)
gs.log(s.name + rpad(s.price.toFixed(2), ' ', 8));
gs.log((10 * s1 + 5 * s2 + s3).toFixed(2));

We've done a few other things in here as well to address her other issues.

Note the addition of the valueOf() method in the prototype. JavaScript will look for, and use, this method anytime it needs to convert an object to a primitive value. By having it return the price, our now-happier developer can get rid of all the .price references she didn't like — JavaScript will know what to do because of the valueOf() method.

We also added a new function to right-justify numbers in a field of fixed length (the rpad() function). This makes it very easy to line up numbers in a right-justified column, which our intrepid-but-frustrated developer wanted.

Finally, we fixed the screwy-looking result of her extended price calculation simply by adding .toFixed(2) to her result. What does that do? And why did we get the screwy result in the first place? This old post of mine explains the why, and also describes one of the solutions: rounding. The .toFixed(2) does the rounding, to two decimal places — which (if you're dealing with American money) is exactly what you need.

When she ran the new code, she saw this:

M 19.98
A 359.99
X 400.00
5819.93

Which she was pretty happy about. My demonic developer diva was breathing considerably easier after our conversation, and she learned a few useful tricks. Hopefully you did, too!