Each()

71 views
Skip to first unread message

Sean Daniels

unread,
Jan 25, 2013, 11:26:08 AM1/25/13
to ra...@googlegroups.com
Is it possible to get the index of the item in each() closures? I want to be able to add logic to the anonymous function based on first or last element.

Ex:

myItems.each(function(item){
if ([this is last item in collection])
dump(item);
});

Bruce Kirkpatrick

unread,
Jan 25, 2013, 1:26:50 PM1/25/13
to ra...@googlegroups.com
You can't use "this" scope to do it probably.  but if you set a variable called "self" or something, it's probably possible.  It would take me a bit of thinking to write actual code like that since it's kind of weird.

I think Ben Nadel wrote some example code of how manually setting "self" would be useful.


The important thing to realize with a closure is that the entire variables scope gets duplicated at the moment you create them.    Because of that, I'm a little concerned about using them unless I could guarantee the variables scope is nearly empty - such as in an  a carefully designed CFC.

You may see better performance if you don't use closures and just store the actual function in the "myItems" CFC instead of using an anonymous function.

// testCFC.cfc
component{
this.arrItem=[1,2,3,4,5];
function each(udfFunction){
   var i=0;
   for(i=1;i LTE arraylen(this.arrItem);i++){
       arguments.udfFunction(this.arrItem, i);
   }
}
}

// test.cfm
function myEachfunction(items, index){
if(arraylen(items) EQ index){
// last one
dump(items[index]);
}
}
testCFC=createobject("component", "testCFC");
testCFC.each(myEachFunction);


if you are running this code somewhere where the variables scope is empty, you could use a closure with 2 arguments and it should have similar performance.  It doesn't necessary matter if the variables scope is duplicated if you don't store large objects in it, but that could have a ton of memory waste in some apps.

testCFC=createobject("component", "testCFC");
testCFC.each(function(items, index){
if(arraylen(items) EQ index){
// last one
dump(items[index]);
}
}
);

To simulate the "this" scope with "Self", I get a bit confused on how to do that and it ends up with an extra function or container of some kind, which is not as simple.

Sean Daniels

unread,
Jan 25, 2013, 1:37:20 PM1/25/13
to ra...@googlegroups.com
Thanks for the follow up Bruce, I think I should have been clearer though. I'm talking about the new each() member function in Railo arrays, structs, queries, etc.

In the example below myItems is an array. Obviously index would have limited use in a struct, but I could see it being useful for arrays (index) and queries (currentrow).

myItems = [1,2,3,4,5]
myItems.each(function(item){
if ([this is last item in collection])
dump(item);
});

> --
>
>

Bruce Kirkpatrick

unread,
Jan 25, 2013, 1:42:16 PM1/25/13
to ra...@googlegroups.com
You could also write your code so that "item" had a reference to the parent object in it.

Bruce Kirkpatrick

unread,
Jan 25, 2013, 1:59:00 PM1/25/13
to ra...@googlegroups.com
I actually tested that before you wrote back by doing this:

 writedump(arguments); writedump(this);  and you'll see there is no other data to use.

perhaps Micha could consider having a different prototype for when you make a closure with 2 arguments.

myItems = [1,2,3,4,5] 
myItems.each(function(items, item){ 
}); 

You are going to have to build something like I did or direct reference the array from request or this scope outside the closure.

Chris Blackwell

unread,
Jan 25, 2013, 2:12:03 PM1/25/13
to railo

fwiw, when you call struct.each() you get passed the key and the value, so it would kinda make sense that array.each() got passed index and value.

Chris

--
 
 

Bruce Kirkpatrick

unread,
Jan 25, 2013, 2:56:25 PM1/25/13
to ra...@googlegroups.com
I wish the "this" scope could work in closures.   If we did have the index, we'd still need to have access to the parent object or to another field "length" to do what the Sean wrote.

myItems.each(function(item, index, parentLength){
if(arguments.index EQ arguments.parentLength){

}
})

or


myItems.each(function(item, index, parentLength){
if(arguments.index EQ arguments.parentLength){

}
})

or

myItems.each(function(items, index){
if(arguments.index EQ arguments.items.length){

}
});


If you can assume the each loop is always in order, then you have to do it this way currently:
c=1;
myItems=[1,2,3];
myItems.each(function(item){
if(c EQ myItems.length){

}
c++; 
});


Bruce Kirkpatrick

unread,
Jan 25, 2013, 2:57:38 PM1/25/13
to ra...@googlegroups.com
Whoops: it should be arraylen(myItems);

Sean Daniels

unread,
Jan 25, 2013, 3:15:42 PM1/25/13
to ra...@googlegroups.com
This gave me an idea, I recalled you can pass arbitrary arguments into your anonymous functions:

myItems = [1,2,3,5]
myItems.each(function(item,total=myItems.len()){
echo(" item:"&item);
echo(" total:"&total);
});

But I still can't get the index value, I tried:

myItems.each(function(item,index=1,total=myItems.len()){
echo(" item:"&item);
echo(" total:"&total);
echo(" index:"&index);
index++
});

But of course index gets reset to 1 in each call. I understand why. Hmmm…

Maybe I ought to just use loop :)

loop array="#myItems#" item="item" index="index" {
if (index == myItems.len())
do something

Bruce Kirkpatrick

unread,
Jan 25, 2013, 3:23:36 PM1/25/13
to ra...@googlegroups.com
I'd say do whatever is easier for the programmer to understand.   Anonymous functions used everywhere in a language like javascript might seem easier and have great encapsulation, but it makes debugging a real pain in the ass.  If you read about programming better javascript, the better code quality convention is to take your functions and make them part of an object and refer to that function by a name.  This way your stack trace is easier to understand.   Some people have analyzed how jquery was written and offer great tips.

This is how most of the javascript is supposed to be written I believe:

function(window){
var javascriptPlugin={
   eachFunction: function(item, index){
        if(index == this.length){

        }
   },
   anotherFunction: function(){}
}

// optionally make your code a global variable
window.myPlugin=javascriptPlugin;

}(window);

Micha and the team have already decided that cfscript should not become javascript, but I don't know what that will mean in the long run.

Adam Cameron

unread,
Jan 27, 2013, 10:24:20 AM1/27/13
to Railo
Relevant to this discussion is Andrew Scott's position on such things
(which also links back to my position on it).

I don't think Andrew has a point... but that's OK: he doesn't seem to
think I do either! Aah... the internet is a great thing.

Anyway, it's all topic reading.

--
Adam

Adam Cameron

unread,
Jan 27, 2013, 10:30:48 AM1/27/13
to Railo
> The important thing to realize with a closure is that the entire variables
> scope gets duplicated at the moment you create them.

Are you sure about that? Is it not rather more a case that a reference
to the specific variable being "enclosed" is made? Not the entire
scope?

I used to think there was value copying going on, but that was not
borne out by experimentation. I discuss it (tangentially) in this blog
article: http://adamcameroncoldfusion.blogspot.ie/2012/12/am-i-right-to-think-this-is-stupid.html

Obviously there's scope for references persisting and making GC not
clear up stuff one might think it should be clearing up, but I don't
think entire scopes are being duplicate? Although I'd love to be
proven wrong, if you've got evidence to support this.

--
Adam

Bruce Kirkpatrick

unread,
Jan 27, 2013, 12:20:47 PM1/27/13
to ra...@googlegroups.com
It is Micha that told me about this.  I haven't tested this much before, so I wrote some code now testing simple values, structs and cfcs.   I found that complex objects are only copying the reference to them.   However, it does make a copy of simple values.   This makes it similar to how structappend(closureVariables, variables) would behave at the exact position when the closure is defined. If you are assigning closures to shared memory, it could waste some extra memory.  It will just depend on how many simple values you are putting in variables scope.

  This code below demonstrates what gets copied or not.  I found that variables aren't copied into the CFC from the outside or inside when you assign a closure to a CFC instance variable.  And they don't get copied from the CFC either.  So it seems like if you assign your closure to a CFC from the outside of the CFC, you can avoid the copying entirely.    

However if you pass a closure as an argument (perhaps more common), it will have access to the external variables scope from inside the CFC.    If you have time to analyze this code, I think you'll find closures to be somewhat dangerous and lead to hidden bugs when scopes are not explicitly defined in addition to the extra memory requirements each time they are used.   I have quite a lot of things in variables scope in my app at the moment.  I don't think optimal code would rely on closures.  Feel free to reuse this info anywhere.

test.cfc
<cfcomponent><cfscript>
variables.test3="test";
function g(){writeoutput(variables.test3&"<br>");}
function g2(f){arguments.f();}
</cfscript></cfcomponent>

test.cfm
<cfscript>
variables.test=structnew();
variables.test.key1="test";
variables.test2="test";
f=function(){ 
writeoutput(variables.test.key1&" is test<br>");
variables.test.key1="test2"; 
variables.test2="test2";
};
f();
writeoutput(variables.test.key1&" equals test2<br>");
writeoutput(variables.test2&" equals test<br>");
testCom=createobject("component", "test"); 

// assigning a closure like this seems to have no drawbacks
testCom.f=function(){ 
writeoutput(variables.test3&"<br>");
variables.test3="test3";
// variables.test is not defined here - uncomment to test that:
//writeoutput(variables.test.key1); // exception: Component [test] has no accessible Member with name [TEST]
};
testCom.f();
testCom.g();
// test passing a closure as an argument instead of assigning - warning: this has different behavior!
testCom.g2(function(){ 
// oh no the variables scope was brought into the CFC now.
writeoutput(variables.test.key1&" is test<br>");
// this variable was defined in the cfc but it doesn't exist.
writeoutput(variables.test3&"<br>"); // exception: Component [Application] has no accessible Member with name [TEST3]
});
</cfscript>

Bruce Kirkpatrick

unread,
Jan 27, 2013, 12:40:18 PM1/27/13
to ra...@googlegroups.com
FIX: The second time it outputs key1, it would actually be test2 since the other closure changed its value.

testCom.g2(function(){ 
// oh no the variables scope was brought into the CFC now.
writeoutput(variables.test.key1&" is test2<br>");

Michael Offner

unread,
Jan 28, 2013, 4:02:02 AM1/28/13
to ra...@googlegroups.com
no, there is no index provided, "each" is a value iterator, to get the "index" perhaps a "for" loop is the better choice.
of course we could provide the index/key as well, for example as second argument, but this would have impact on performance, so this should not happen by default.
in Railo 4.1 all each functions (each,arrayEach,structEach ...) now taking 3 arguments:
each(collection[,parallel[,maxthreads]]), of course we could add a 4th argument for this or even move this at the second place.

Micha




2013/1/25 Sean Daniels <daniel...@gmail.com>

--





--
/micha

Michael Offner CTO Railo Technologies GmbH

Igal Sapir

unread,
Jan 28, 2013, 4:05:30 AM1/28/13
to Railo List

+1 for optional index.  I'd put it at 2nd place.

This will be very intuitive to developers who are familiar with the very popular query js library (IIRC)

Igal

--
typos, misspels, and other weird words brought to you courtesy of my mobile device and its auto-(in)correct feature.

--
You received this message because you are subscribed to the Google Groups "Railo" group.
To unsubscribe from this group, send email to railo+un...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

Bruce Kirkpatrick

unread,
Jan 28, 2013, 12:07:08 PM1/28/13
to ra...@googlegroups.com
That's very nice that you are adding more concurrent programming features to the language.

Adam Cameron

unread,
Feb 5, 2013, 4:11:33 AM2/5/13
to ra...@googlegroups.com
There's been a bit of a delay here whilst I was sick and unmotivated.

Anyway, your code doesn't seem to really have any bearing on what you were saying Bruce? I don't see how that code demonstrates variables being duplicated.

I did some investigation of this, and bunged it in my blog. Could you pls have a gander and perhaps elaborate on what you mean, in the context of my sample code. I suspect we are talking at cross purposes.


--
Adam

Bruce Kirkpatrick

unread,
Feb 5, 2013, 9:08:31 AM2/5/13
to ra...@googlegroups.com
I can't reproduce the output that I said I had when running that code I put on here.  I also had deleted those files, so I don't know if it was their location that caused this.  I must have been blind or something.   I can't see that there are copies happening anymore.  I'm going to have triple check myself.  It's hard to believe I would have posted all that if it wasn't happening.
Reply all
Reply to author
Forward
0 new messages