Looping over an array in CFScript

Thursday, October 29, 2009

The other day, I was searching the cf9 docs for an equivalent of the <cfloop array=”#myarray#”> functionality introduced in CF8, and it appears that was not introduced. So it was back to ArrayLen() looping for me. Tonight, I saw TWiCF’s Micky Dionisio put this out on the Twitters, and I thought it was cool:

“#coldfusion 9 tip -grab a handle on the iterator of an array by calling someArray.iterator(). then u can use that in script FOR/WHILE loops!"

I never thought of that, so I wanted to give it a whirl.

<cfscript>
//pre-cf8
list = "one,two,three,four";
a = listToArray(list);
for(i=1; i LTE ArrayLen(a); i=i+1){
	writeoutput(a[i]);
}

//traditionally, with cf8+ syntax
a = ["one","two","three","four"];
for(i=1; i <= ArrayLen(a); i++){
	writeoutput(a[i]);
}

//with an iterator
//uncomment if you want to see what's available: writedump(a.iterator());
iter = a.iterator();
while(iter.hasNext()){
	el = iter.next();
	writeOutput(el);
}
</cfscript>

Warning: don’t try to skip the iter = a.iterator() and el = iter.next() steps in an effort to save a few lines of code! If you try to loop while(a.iterator().hasNext()), you’ll get stuck in an endless loop because you’ll be returning a new iterator each time. And each time you call iter.next(), you’re advancing through the array; thus, doing writeOutput(iter.next()); doSomethingWithThisElement(iter.next()); will give you very different results from what you’re expecting. You’ve been warned.

Thanks for the tip, Micky!

9 comments:

Henry Ho said...

I wish CF9 add the for..in loop support for array. Looping an array is easier using <cfloop array=""> than cfscript.

bill shelton said...

good tip,marc! challenged me to see if i could make it smaller ... i wonder just how small a cf loop could be?

for(i=-1;++ilt;myarray.size();) {
...myarray.get(i);
}

Micky Dionisio said...
This comment has been removed by the author.
Micky Dionisio said...

Mark Mandel pointed out that this may be slower since it uses reflection via JavaProxy. Something to consider!

-Mick

Micky Dionisio said...

last post on this promise.

using a java arraylist with iterator is 500x faster than cf's for loop and cf for loop using iterator.

createobject("java", "java.util.ArrayList");

-Mick

Marc Esher said...

great information guys. Thanks.

Micky, your friend needn't decompile if he simply wants to see the inheritance tree, available methods, etc. I whipped this up really quick to give a quick and dirty view of a CF Array, but you could use this for just about any cf datatype. note this is literally about 15 minutes of work, just to give me a fast idea of what's what:


<cfscript>
a = ["one","two"];
writedump(var=getMetadata(a),expand=false);
s = {key1="hi mom",key2=a};
writedump(var=getMetadata(s),expand=false);
writedump(createDirtyStructView(a));
writedump(createDirtyStructView(s));

public struct function createDirtyStructView(someVar){
var cls = someVar.getClass();
var data = {className=cls.getName(),exceptions=[],methods=[],interfaces=[],parent=cls.getSuperClass().getName(),tree=[]};
var classes = cls.getClasses();
var methods = cls.getMethods();
var interfaces = cls.getSuperClass().getInterfaces();
var i = 1;
//writeDump(methods);
for(i=1;i<=ArrayLen(classes);i++){
arrayAppend(data.exceptions,classes[i].getName());
}
for(i=1;i<=ArrayLen(methods);i++){
arrayAppend(data.methods,methods[i].getName());
}
for(i=1;i<=ArrayLen(interfaces);i++){
arrayAppend(data.interfaces,interfaces[i].getName());
}

var thisParentClass = cls;
var thisParentName = thisParentClass.getName();
arrayPrepend(data.tree,thisParentName);
var sentry = 100;
while(thisParentName != "java.lang.Object" && i < sentry){
thisParentClass = thisParentClass.getSuperClass();
thisParentName = thisParentClass.getName();
arrayPrepend(data.tree,thisParentName);
i++;
}
return data;
}
</cfscript>

Micky Dionisio said...

@Marc

Once again, another great tip to file away. very cool!

-Mick

FuzzySiberians said...

just a thought : you don't have to convert the list into an array. you can also use:

list = "ten,eleven,twelve";
for (j=1; j lte ListLen(list); j++) {
writeOutput('...');
writeOutput(listGetAt(list,j) & '
');
}

edwardbeckettx said...

Useful function Marc ... Thanks ;-)