SlightlyLoony
Tera Contributor

find_real_file.pngThere are occasions when it is quite useful to have a function argument that isn't an actual value (in the usual sense of a number, string, etc.), but rather a way to get a value. There are also occasions when it would be useful to have a function argument that specifies an operation that should be performed. This is one of those topics that's easier to explain with an example than with English — so consider this little code snippet:


gs.log('Longest computer name: ' + forAll(computers, 'name', longest));
gs.log('Last computer name: ' + forAll(computers, 'name', last));

When I run it on my Service-now.com instance, it prints:

*** Script: Longest computer name: snc32dstanojevic
*** Script: Last computer name: vc01

How does this work?

First let's take a look at that forAll() function:

function forAll (getGR, field, op) {
var answer = null;
var gr = getGR();
while (gr.next())
answer = op(answer, gr.getValue(field));
return answer;
}

Look carefully at how this function uses the arguments getGR and op — they are called as functions, not as what you normally see for an argument: a value. In other words, they are a way to get a value, not the actual value itself. In the case of getGR, it's expecting a function that returns a GlideRecord that's ready to be iterated through. For op it's expecting a function that takes two arguments: a past value and a current value, and it returns a new value based on those two — it's an operation, or op for short. All this function does is iterate through the glide record, applying the given field's value to the operator and returning the final result.

Now here's the whole thing:

gs.log('Longest computer name: ' + forAll(computers, 'name', longest));
gs.log('Last computer name: ' + forAll(computers, 'name', last));


function longest(past, now) {
var jsNow = '' + now;
if (!past)
return '' + jsNow;

return (jsNow.length >= past.length) ? jsNow : past;
}

function last(past, now) {
var jsNow = '' + now;
if (!past)
return '' + jsNow;

return (jsNow >= past) ? jsNow : past;
}

function computers() {
var gr = new GlideRecord('cmdb_ci_computer');
gr.addNotNullQuery('name');
gr.addQuery('name', 'NOT LIKE', '%@%');
gr.query();
return gr;
}

function forAll (getGR, field, op) {
var answer = null;
var gr = getGR();
while (gr.next())
answer = op(answer, gr.getValue(field));
return answer;
}}


The function computers returns a GlideRecord instance containing all the computers that have a name, as long as that name doesn't contain a "@" character. It's intended for use as the first argument to forAll. The longest function returns whichever of its arguments has the most characters; when used as the third argument to forAll it results in the longest computer name in the entire collection. The last function is very similar, except that it returns whichever argument is last in alphabetical order — and when used as the third argument to forAll results in the computer name that's last overall in alphabetical order.

Note the syntax for all these uses of the function. Where it's being used as the argument itself, you must leave off the parentheses ("()" — otherwise, Javascript will try to evaluate the function instead of passing it. On the other hand, insider forAll where the functions are actually being used, they must have the parentheses so that JavaScript will evaluate the function there.

If you find yourself writing scripts that are very similar except for the way in which they get their values, or except for the what operation they perform, consider function arguments as a design approach. This example shows somewhat contrived examples of each use.

Function arguments can simplify your code considerably when used well...

3 Comments