Saturday, July 17, 2010

CF: The varExists() function -- Expanding IsDefined() to work with regular array and associative array notations.

Many times while working in ColdFusion code I am encountering the simple need to check for the existence of an index in an array, e.g. myArray[3]. While other languages may have had easier checks on this, in Adobe ColdFusion, I unfortunately, have to work around this scenario with many lines of code or different functions to call. Thus, I am able to construct fully well formed variable references and use them, but cannot check their validity in a simple fashion.

For example, I can't do a simple call to a universal function such as IsDefined("myVar[3]"). In this simple scenario we could have used ArrayIsDefined(), however, the scenarios I normally deal with are not so simple. In my scenarios I am not sure about whether "myVar" is an array in the first place or whether the index is numeric. E.g. I may need to check myVar[checkIndex]; this maybe a strucure with an associative array notation which takes me back to square one. Along these lines, multi-dimensional arrays such as myArray[3][4] are equally unqualified for ArrayIsDefined().

Similarly associative array notations that can be used with structures, e.g. myStruct["testNode"], equally do poorly on these checks and could require some sort of special checking and more lines of code.

How about a combination of things:
TestStruct.Animals['species'][1]['counts'][2]

All of these scenarios go beyond the ability of IsDefined() or other functions and cause more code to be written. Maybe this would be an opportunity to expand in future version of ColdFusion but for now we're stuck or are we? (yeah, rhetorical question this one).

I made this a test project and developed an alternative function, varExists(), to IsDefined(); it may not be usable in all scenarios but will come in handy in coding.
The sample implementation can be downloaded here (varExists.zip (3KB)).

The first objective was just to be able to handle the complex notation scenarios that commonly fail under IsDefined().

e.g.:
varExists("TestStruct.Animals['species'][1]['counts'][2]") should operate without a hitch on complex variable notations. While at the same time maintaining full backward compatibility with IsDefined(). So you should be able to do things like this varExists(varName), varExists should check for the existince of the content referenced by the content of varName, e.g. a string such as "TestStruct.myArray[2][1]".

But, why stop there, the next thing that I commonly do once I check for the existence of a variable is to retrieve its value for some sort of operation, so I added this option as second argument. Thus, varExists will automatically return the value referenced as part of its operation if "True" is passed as second argument, e.g. varExists("myUserArray[33]",true). This would return the UserName at array index position 33.

Another common task I perform when the value is not known or "undefined" is to assume a default value. Thus, I added this as third parameter. When a third parameter is supplied it will be returned, when the actual variable reference is undefined.
For example:
varExists("myUserArray[33]",true,"John Doe")
would return "John Doe" if the array index 33 is not populated for some reason.

The example code has the test array and unit tests with sample scenarios. The hope is that eventually the ColdFusion language can accommodate these type of operations natively.

Happy experimenting.

Cheers,

B.

Addition:

The Railo CFML engine does not exhibit the flaw outlined in this article. Thus, if you use Railo, you probably will not encounter scenarios in which you can construct a valid variable reference but cannot check for its existence. The IsDefined() function works as expected, however, if you want the expanded functionality of retrieving variable values etc. you would still need to use the varExists() User Defined Function (UDF) provided here.

Best,

B.

No comments: