Using sgn() To Clamp Values In Array Sorting Operations In ColdFusion
A couple of years ago, I ran into an issue with array.sort()
in Lucee CFML wherein it threw an error if my sort operator returned a value that was larger than a signed integer. Yesterday, Adam Cameron left a comment on that post about using the sgn()
function, in ColdFusion, to fix the issue. To be honest, I don't believe I even knew that the sgn()
function existed. So, in order to help fill in my lacking mental model, I wanted to quickly look at what the sgn()
function does.
The sgn()
function takes a numeric input and returns one of only three values:
-1
: The input was less than0
.0
: The input was0
(ornull
in Lucee CFML).1
: The input was greater than0
.
It just so happens that these are also the three documented, usable values for the .sort()
functions in ColdFusion.
Now, let me quickly demonstrate the issue I was running into my previous post. Imagine that I have a list of database tables along with their current AUTO_INCREMENT
primary key values. If I wanted to sort the the collection of tables based on their primary key, I could do something like this:
<cfscript>
// NOTE: Max signed INT value in Java is 2147483647.
tables = [
{ name: "account", pkey: 64004394 },
{ name: "company", pkey: 5307 },
{ name: "file", pkey: 4947493641 },
{ name: "file_folder", pkey: 3147493648 },
{ name: "project", pkey: 839409903 },
{ name: "share", pkey: 99347337683 },
{ name: "user", pkey: 64004394 }
];
// Store tables by their primary key, descending.
tables.sort(
( a, b ) => {
// If the primary keys in the two tables are the same, fall-back to using a
// name-based sort in order to create a predictable outcome.
if ( a.pkey == b.pkey ) {
return( compareNoCase( a.name, b.name ) );
}
return( b.pkey - a.pkey );
}
);
dump( tables );
</cfscript>
The max signed int
in Java is 2,147,483,647
. In Lucee CFML, the .sort()
operator return value must fit into a signed int
. However, because my primary key values are so large, in some cases, the maths in my return
statement:
return( b.pkey - a.pkey );
... will generate a value that is larger than a signed int
. And, running this in Lucee CFML 5.3.8.201 will throw the following error:
lucee.runtime.exp.FunctionException: invalid call of the function ArraySort, second Argument (function) is invalid, return value of the function [lambda_gj] cannot be casted to an integer.
To fix this problem, as Adam Cameron pointed out, we can simply wrap our return value in sgn()
to clamp the possible return values to -1
, 0
, and 1
:
<cfscript>
// NOTE: Max signed INT value in Java is 2147483647.
tables = [
{ name: "account", pkey: 64004394 },
{ name: "company", pkey: 5307 },
{ name: "file", pkey: 4947493641 },
{ name: "file_folder", pkey: 3147493648 },
{ name: "project", pkey: 839409903 },
{ name: "share", pkey: 99347337683 },
{ name: "user", pkey: 64004394 }
];
// Store tables by their primary key, descending.
tables.sort(
( a, b ) => {
// If the primary keys in the two tables are the same, fall-back to using a
// name-based sort in order to create a predictable outcome.
if ( a.pkey == b.pkey ) {
return( compareNoCase( a.name, b.name ) );
}
// The sgn() function will only ever return -1, 0, 1 for a given input. As
// such, we can clamp our delta down to a value that will always work for the
// sort operation.
return( sgn( b.pkey - a.pkey ) );
}
);
dump( tables );
</cfscript>
Now, when we run this ColdFusion code in Lucee CFML 5.3.8.201, we get the following output:
As you can see, with our sgn()
safety-net in place, our .sort()
operator runs perfectly well.
Huge shout out to Adam Cameron for bringing the sgn()
function to my attention. It seems like it provides a perfect intent for the sort functions in ColdFusion. I can't immediately think of any other reason to use this function. But, now that I know that it exists, I'll be on the look-out.
Want to use code from this post? Check out the license.
Reader Comments
Just learned from my co-worker, Paul Rehkugler, that JavaScript has
Math.sign()
for this same type of functionality.Funny that it handles
null
but notundefined
. I guess I could sort of see that.I was today years old when I learned about the
sgn()
andMath.sign()
functions. It's always helpful to see a useful use case too. Helps lock it in. I may have even seen these before, but without the use case they vanish because they're too esoteric otherwise.@Chris,
Exactly! That's how I feel about a lot of the
Math
functions. Until I actually have a use for them one day, I refuse to know that they exist 😆 I mean, what the heck is an arc-tanget anyway?! (looks over shoulder to make sure 9th great trigonometry teacher isn't standing there).@Ben, an arctangent is common knowledge lol🤪
@Anthony,
Ha ha, suuuuuure it is 😉
Post A Comment — ❤️ I'd Love To Hear From You! ❤️
Post a Comment →