Difference between for and each

In this code what is the difference between using a for or each function ?
Which is better?

for (dev in settings.speaker) {
            dev.initialize()
            if(logEnable == true)log.info "${app.label} doing the busness on $dev"
            pause(3000)
        }
/*        
    settings.speaker.each { it ->
      it.initialize()
        if(logEnable == true) log.trace "${app.label} initializing $it"
        pause(3000)
    }

The second is less typing. :slight_smile: (Especally if you forget the it -> at the beginning and just let that part be done for you automatically.)

The first comes from Java, and most Java is valid Groovy. The each() method is a Groovy addition that, coupled with a closure, lets you do something similar with less typing and in what Groovy proponents would call a more Groovy-esque manner. (I also happen to think it's easier to type and clearer to read.) But that's more or less why both exist.

What differences there may be by the time either gets compiled to Java bytecode, I can't say...

2 Likes

Thanks for that, I was think which is more efficient for the hub if you were doing a lot of repeats

I suppose we can't know for sure without benchmarking on the JVM that Hubitat apps/drivers run in, which there isn't an easy way to do. If I had to guess, I'd say the Java way probably incurs slightly greater efficiency (though probably less so than if things were statically compiled). However, from a practical perspective -- the collections you'd be iterating over in an app and driver are likely to be rather small, apps and drivers generally only run for a few dozen or maybe a few hundred milliseconds at a time, and the hub sits idle most of the time -- I can't imagine it would really make a difference. I go with the one that is easier for me to write and maintain, so my preferences is each(). :slight_smile: (Oh, and see also: that "premature optimization is the root of all evil" quote.)

Some users have reported that avoiding def/Object typing where possible is faster/more efficient on Hubitat (it probably is in general, but this specific advice comes from the current maintainer of webCoRE, perhaps the largest community app--or app at all--on Hubitat, so I'm sure he has numbers to back this up). Thus, if you know you're dealing with devices, probably the best thing you can do with either is specify the object type, something like:

    settings.speaker.each { com.hubitat.app.DeviceWrapper dev ->
      dev.initialize()
      if(logEnable == true) log.trace "${app.label} initializing $dev"
      pause(3000)
    }

But even then I probably wouldn't worry too much about this for most apps.

2 Likes

What does the device wrapper do, I try to use int/String/float but don't really understand that wrapper bit

That is the way you specify the type of the object (you could have kept the name it if you wanted, but without a type specified, it's just an Object--not a String, Integer, etc. as it sounds like you are already familiar with; specifically typing objects is what I'm saying others have identified as helpful in larger apps/drivers).

What might not be apparent is that com.hubitat.app.DeviceWrapper is the "full" name of the DeviceWrapper class (which you have to use unless you do something like import com.hubitat.app.DeviceWrapper elsewhere, which I normally do to make this easier to type). It may also not be apparent that DeviceWrapper itself exists at all, but it's what you get with app inputs that select devices (vaguely alluded to in the docs; also note that with multiple: true, you get a DeviceWrapperList that contains individual DeviceWrapper objects).

Closures (2nd case) is interesting in the JVM. This is a common 'issue area', so if you have choices of minimizing closures, it may help....

So perfect would be

Com.hubitat.appDeviceWrapperList settings.speaker.each { com.hubitat.app.DeviceWrapper dev ->
dev.initia.......

I've read a lot about closures but still don't really understand what they are and how to avoid?

No, you do not need the type before settings.speaker (that object is already defined).

So whenever you want to call a dev from a setting you should use this

Eg
com.hubitat.app.DeviceWrapper speaker.initialize()

This would trigger initialize on all devices selected, at the same time?

No, if the object you're referencing is already defined (as anything that comes from an app input is, and presumably that is what speaker is in your context), you do not need to specify the type. That is when you are creating/defining the variable.

You can also just totally ignore the advice about typing as opposed to def and whatnot, too--for small apps/drivers, it's unlikely to make much of a difference, just something nh.schottfam noticed. Most apps aren't webCoREian in size. :smiley: And it's what you'll find in most Groovy examples.

2 Likes

I get it, it because dev -> is a new variable so defining its object type

Can you elaborate/ explain , are you saying the 1st or 2nd example is better?

closures can be starting points for memory leaks in older jvms , so for code that runs a lot, need to be careful with them and keep them 'simple' as much as possible.

so which was better to reduce chance of memory leak?

avoid closures or keep them simple

so like the the live code is safer than the commented out code?

private zwaveEvent(hubitat.zwave.commands.thermostatmodev2.ThermostatModeReport cmd ) {
    String mode = ''
    switch (cmd.mode) {
    	case 0x01:
            mode = 'heat'
            break;
        case 0x00:
            mode = 'off'
            break;
        case 0x0F:
            mode = 'emergency heat'
            break;
        case 0x0B:
            mode = 'cool'
            break;
        default:
            log.warn 'no mode'
    }
/*    
    if (cmd.mode == 0x01){ //1 normall heat 0x01 1
    	mode = 'heat'
    }
    else if (cmd.mode == 0x0F){ //15 boost 0x0F 15 == true
    	mode = 'emergency heat'
    }
    else if (cmd.mode == 0x0B){ //11 eco 11 0x0B 11
    	mode = 'cool'
    }
    else if (cmd.mode == 0x00){ // 0 off 0x00 0
    	mode = 'off'
        sendEvent(name: 'thermostatOperatingState', value: 'idle')
        
    }
    else{log.warn 'no mode'}
*/
    sendEvent(name: 'thermostatMode', value: mode)
}

those don't appear to be closures

so either or