views:

32

answers:

3

Does groovy support any kind of nested iterator notation?

In the example below, I want to somehow get the projectName value, that came from the outer iterator, into my inner iterator. Is this possible without storing in a variable? In my example I get a runtuime error that "project" is not found

it.myprojects.project.each{
     println("Project name: " + it.projectName)
    it.myroles.role.each{
         Role role = new Role()
             role.setName(project.projectName)//how do I get projectName here without storting it in a variable in the outer loop?
    }
}
A: 
 it.projectList.each {...}

?

and this: http://groovy.codehaus.org/Looping.

You loop on the list, not on the thing in the list. Seems from your code you are looping on the thing in the list.

hvgotcodes
err, thats my bad on the syntax..i am actually pulling this from an XML file and I had some tags called "projectlist" and "rolelist" so the word list is just a coincidence
Derek
edited the code now so there is no confusion
Derek
@derek is there still an issue? Fire it up in your favorite debugger and see what project is...aslo, I noticed the first token in your code is 'it', so it seems like there is some stuff going on outside the code you posted that you might want to share.
hvgotcodes
@Derek: edited code *still* looks wrong - if myprojects is a collection of projects, then the each closure should be applied to that collection
Michael Borgwardt
+3  A: 

Those it variables are not iterators, they are closure parameters. The name it is not short for "iterator", it literally means "it", used as a default name for single-parameter closures. However, you can use explicit (and thus different nested) names like this:

it.myprojects.project.each{ project ->
     println("Project name: " + project.projectName)
     project.myroles.role.each{ role->
         Role r= new Role()
         r.setName(project.projectName)
    }
}

Actually, I'd advise against using the each method at all and use real loops instead:

for(project in it.myprojects.project){
     println("Project name: " + project.projectName)
     for(role in project.myroles.role){
         Role r= new Role()
         r.setName(project.projectName)
    }
}

This is better because closures make the code much harder to debug, and also potentially slower. And in this case, there isn't really any advantage in using closures anyway.

Michael Borgwardt
ah - that is confusing. The code definitely works if you keep re-using "it" in a nested fashion. It only became apparent to me there was something funny going on when I tried to make a back-reference to the previous one
Derek
@Derek: It sounds like you're doing cargo-cult programming, i.e. trying to use the syntax without understanding what it means. No wonder you're confused. You really need to read up on closures till you understand that "each" is a method, not a language keyword, the code in the curly braces is a closure passed as parameter to the method, and the "it" is the implicit parameter of the closure when it's called by the each method.
Michael Borgwardt
I don't see why an explicit loop would be any better than using the each method - it is more a case of personal style and preference.
mfloryan
@mfloryan: You wouldn't say that if you'd ever seen a stack trace thrown inside a thrice-nested closure. They may be syntactically elegant, but on the JVM (at least right now) they require very elaborate hacks that bloat byte code and stack traces, and which debuggers (at least the one in eclipse) can't handle well.
Michael Borgwardt
OK. I agree in this particular case the low-level implementation details do make a difference (and yes, I have seen such stack traces and have decompiled the bytecode so I have a rough idea what it looks like). Still it is just a transient problem and likely to have negligible performance implications but allowing more expressive and cleaner code.
mfloryan
@mfloryan: except there isn't anything more expressive or clean about each{} compared to a simple loop - the closure causes problems and provides no benefit, hence my advice to prefer the alternative.
Michael Borgwardt
+2  A: 

It feels like this should work:

it.myprojects.project.each{ project->
     println("Project name: " + project.projectName)
     it.myroles.role.each{
         Role role = new Role()
         role.setName(project.projectName)
    }
}

Also, you can reference the outer closure using the owner variable

it.myprojects.project.each{ 
     println("Project name: " + it.projectName)
     it.myroles.role.each{
         Role role = new Role()
             role.setName(owner.projectName)
    }
}

However, I don't see a reason for iterating over roles if all you seem to be doing is creating new instances of the Role class. Perhaps something like this will be simpler:

it.myprojects.project.each{ 
     println("Project name: " + it.projectName)
     it.myroles.role.size().times {
         Role role = new Role()
         role.setName(owner.projectName)
    }
}
mfloryan
+1 and selected for actually answering the question I asked
Derek