Looks like I tend to start these articles with a joke. I wanted to have an infinite loop joke for this one, but I’m afraid you’ll never hear the end of it…
Honestly, building this case is almost too easy. We have already discovered various ways in which “for” loops tend to fail in our previous two articles. The thing is, those two are not enough to cover the whole story. Classic “for” loops are clumsy, not easy to write, loaded with potential failure points, and combining them with “var” variables leads to the programming equivalent of chaos.
Their brethren – for/in and for/of (the latter still way too modern, with all its glorious 2015-ness, to be in ServiceNow), although a tad more intelligent, still suffer from pretty much the same flaws.
Now I want you to forget the title. We won’t be wasting any more time with the “for” loops. I won’t even provide an example. What we’ll be focusing on are the alternatives. There are a lot of those, mostly revolving around arrays, packing some cool features that will make it easier to cure your natural inclination towards classic “for”-s.
The first and most obvious choice is the good old “while” (or “do/while”). We’re all using it daily, it’s powerful, and most importantly – it doesn’t explicitly bind you to a certain data type, like we’ll see with the other loop alternatives. That makes it a universal option for various logical constructs. It gives you more control over what’s happening, and if you need even greater freedom, you can always start it with a “while(true)” and define your own rules over when it should break out. One thing to watch out for here – a badly defined break logic can easily send the “while” into an endless loop.
Now the good stuff – array methods. We’ll try to cover all that are available in ServiceNow. Similar ones can also be found in the ArrayUtil API, which we’ll check out in its own article. Before going into details, I’ll start with the one (and maybe only) real flaw that these methods have. It’s right there in their name. They only work on arrays, that’s it. So, if you want to use them, you’ll have to base your code more around array structures (if you’re not doing that already).
Having said that, let’s turn our attention to the positive side. Here are some of the benefits of using array methods. We’ll also check out some examples afterwards:
- Immutability, or at least a certain level of it. Most array methods return a new array when they process the original one. Modifying your function input data is considered a bad practice.
- Clear responsibilities – each method will give you a different level of control, so if you always use the least powerful one that covers your need, you’ll have little to no unexpected results.
- No functionality disadvantage compared to “for” loops – when processing an array, there’s nothing a “for” can do that array methods can’t. That includes having access to the current index number as an argument if the callback function.
“This” context – as we’ve covered in other articles, all loops tend to lose their focus in certain situations. The most common one in a ServiceNow environment is overriding the “this” context in a script include. Handling it is somewhat complex in a “for” loop, but array methods have a neat little trick up their sleeves – you can just pass a “this” context as a second argument after the callback function. It’s a solution beautiful in its simplicity.
Let’s check out some examples. They will all use the same sample array:
var sampleArray = [1, 2, 3, 4, 5]
Now let’s look at one of the array methods in its simplest form:
var processedArray = sampleArray.filter(function(e) {
return e > 2;
})
gs.info(processedArray);
Here you can see the limited responsibility I mentioned above. The .filter() method does exactly what its name suggests and nothing else – it filters out all the elements of the original array that don’t pass a certain test. In this case, all elements smaller or equal to 2 are filtered:

Time to step it up a bit:
var processedArray = sampleArray.map(function(e, i) {
return e + i;
})
We include the index number in the game. The .map() method modifies each element in the way you specify in the callback. In our case, we add the index number to the element value. Here’s the output:

The next one is a real challenge to any novice dev:
var processedArray = sampleArray.reduce(function(accumulator, current, index) {
accumulator += 'Index ' + index + ': ' + current +'; '
return accumulator;
}, '');
The .reduce() is not just an array method, it’s the swiss army knife of array methods. It can do everything the .filter() and .map() can and more. Its main purpose is reducing the original array to something else – not necessarily a new array. It takes an additional argument after the callback – it’s the data type we’re returning – basically, that’s how we specify what we want to reduce the array to. In our case, we’re reducing an array to a string containing its elements and index numbers.
The callback function also has an additional argument this time – the accumulator. That is the actual variable that the method will produce in the end. That’s why it’s usually called an accumulator – it grows after each iteration with the result returned by the callback. Here’s our output:

Mastering the .reduce() will add a powerful tool to your coding arsenal. Now we will try to turn that array into an object instead:
var processedArray = sampleArray.reduce(function(accumulator, current, index) {
accumulator[index] = current;
return accumulator;
}, {});
gs.info(JSON.stringify(processedArray))

Let’s take a step back now. Let’s say all of those are a bit too much for you, and you need something a bit more familiar to start with. There’s an array method for that case too:
sampleArray.forEach(function(e, i) {
gs.info('Index ' + i + ': ' + e);
})
This one works just like a “for” cycle, but the use of a callback function prevents most of its related issues. Here’s the output:

Let’s make things a bit more complicated, just for the fun of it. Here’s a script include I borrowed from another article of mine:
var TestyMcTestface = Class.create();
TestyMcTestface.prototype = {
initialize: function() {
},
logger: function(number) {
gs.info('Simple test! ' + number);
},
process: function() {
var someArray = [1, 2, 3];
someArray.forEach(function(e) {
this.logger(e);
});
},
type: 'TestyMcTestface'
};
Calling the process() method here produces no output, due to the “this” context issue I mentioned earlier. In the other article I’ve shown a way to bind a context to the callback function. Well, it turns out there’s even a simpler way to deal with that:
process: function() {
var someArray = [1, 2, 3];
someArray.forEach(function(e) {
this.logger(e);
}, this);
},
Simply adding the “this” context of the script include as a second argument to the .forEach() method deals with the issue. The cool thing about it? It works the same for all the other array methods. This is the result:

Here is a little bonus that will probably save you some lines of code in the future:
var sampleArray = [
1, 2, 3, 4, 5
]
var negativeValidation = sampleArray.every(function(e) {
return e > 0;
})
The .every() method puts every element of an array through a test, specified in the callback. It returns a boolean value. In our case it will check if all elements are bigger than zero and will return true. If even one element fails the test, we get a false. Perfect for validations.
var sampleArray = [
1, 2, 3, 4, 5
]
var negativeValidation = sampleArray.some(function(e) {
return e < 0;
})
This is .some(). It’s the twin function of .every() – it will check if at least one of the elements passes a test and return true if it does. Only if all of them fail would we have a false, like in this case.
I’m ending this one in a similar fashion as the “if” statement article – “for” loops cannot and should not be completely discarded as a tool. What you can do though, is at least use “for/in” instead of the most basic, classic form of the “for” cycle, in case there is no other alternative. Nevertheless, most of the time, such an alternative exists. Code devoid of “if” statements and “for” loops might not be perfect, but it’s certainly a good start.
Discover the whole “for” series and more insights about ServiceNow in our blog.