Skip to main content
Ben Nadel at CFUNITED 2010 (Landsdown, VA) with: Chris Peters
Ben Nadel at CFUNITED 2010 (Landsdown, VA) with: Chris Peters

Using sgn() To Clamp Values In Array Sorting Operations In ColdFusion

By
Published in Comments (5)

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 than 0.
  • 0 : The input was 0 (or null in Lucee CFML).
  • 1 : The input was greater than 0.

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:

Array of database tables, sorted safely by primary key in ColdFusion / Lucee CFML.

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

15,848 Comments

Just learned from my co-worker, Paul Rehkugler, that JavaScript has Math.sign() for this same type of functionality.

Math.sign( -1.5 );  // -1
Math.sign( 1.5 );    // 1
Math.sign( 0 );       // 0
Math.sign( null );   // 0
Math.sign( undefined );   // NaN

Funny that it handles null but not undefined. I guess I could sort of see that.

238 Comments

I was today years old when I learned about the sgn() and Math.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.

15,848 Comments

@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).

Post A Comment — I'd Love To Hear From You!

Post a Comment

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel