Groovy: Finding Objects In A List By Property Value

My streak of coming up with the longest, most awkward titles possible for my posts on Groovy programming remains! Given the recent happenings in the world, I was tempted to make a post on how to call Log4j from Groovy, but instead I figured it would be better to do a post on something I actually needed to do recently.

I found myself trying to use the API for a vendor of ours to determine how much data had been used by each piece of networking equipment that vendor provided over the past hour. The only problem is that the API itself doesn’t provide any data use information for the infrastructure devices; that information is only given in terms of the clients connected to the devices. However, each client also has properties for the unique ID and the hostname of the infrastructure device it’s connected to. My process worked out as follows:

  1. Create an empty list of devices.
  2. Gather a list of every client from the API.
  3. Using the device ID that client is connected to, see if that device is already in my device list from step 1.
  4. If yes, store the current client ID on the device object.
  5. If no, create a new device object which stores the client ID of the current client and add it to the list of devices.
  6. Repeat steps 3–5 until the entire list from step 2 has been processed.
  7. Go through each device, gathering the data usage for all of the clients connected to it.

The steps may seem a little odd, but trust me that this order of operations was necessary due to the architecture of the API I was getting the data from.

To keep myself sane, I made an extremely simple class for each device. I didn’t even need any methods for it, just constructors:

// Class to store each device, connected clients, and data used. class Device {
long deviceID
String hostname
long dataUsed = 0
List clientIDs = []
// Constructor.
Device( long deviceID, String hostname ) {
this.deviceID = deviceID
this.hostname = hostname
}
// Overloaded constructor when we have information on the first connected client.
Device( long deviceID, String hostname, long clientID ) {
this.deviceID = deviceID
this.hostname = hostname
this.clientIDs << clientID
}
}

Along with the ID, hostname, and data use, I also tracked a list of client IDs connected to that device so that later I could easily both get the data for each connected client in one API call and calculate the average amount of data used per connected client.

For the sake of example, let’s say I had the following two lists:

List allClients = GetAllClientsFunction()
List deviceList = []

The allClients list has each client in the environment stored in it. The deviceList list is, at this time, empty. However, each client object has a property for device_id that is the glue mapping what that client is connected to with the devices that will be stored in deviceList.

My real challenge was in step 3 from above where I needed to take my existing list of devices and determine if the device the current client was connected to already exists in deviceList or if it needed to be added. The brute force way would be to create a nested for loop - not literally nested since it's inside of a closure, but you get the point - checking to see if the device_id property of the current client matched the current device in deviceList. That seemed extremely inelegant, though, so I went digging to see if I could find something better.

Fortunately for me, I found that lists in Groovy have a find method to facilitate this. Note that there are many flavors of this based on if you want to find a whole object, the index of it, a key or value if it's a map, etc. Check out this post if you want to see other examples. In my case, I was able to use the following to iterate through my client list and match up the devices:

allClients.each { singleClient ->
// Check if the device the client is connected to is already known.
def currentDevice = deviceList.find{ it.deviceID == singleClient.device_id }
if( currentDevice == null ) {
// Create a new device object and add it to allDevices
} else {
// Add the data use from this client to currentDevice.
}
}

The extremely important line is:

def currentDevice = deviceList.find{ it.deviceID == singleClient.device_id }

This instantiates a new variable containing the information about the device with a deviceID property matching the device_id property of the current client. There are a couple of really nice features with this. First, if there isn't a match, the value of currentDevice is null. Thus, if my value is null then I know I need to create a new device object for my deviceList list. Second, and even more importantly, if there's a match the value in currentDevice is not a copy of item from deviceList. Rather, it's a reference to that item, meaning that any changes made to currentDevice will be reflected on the item contained within deviceList. I was able to easily shift the new client ID value from singleClient onto the clientIDs property of currentDevice, and that information will still be stored on the object in deviceList because it's the same object.

Originally published at https://looped.network on December 21, 2021.

--

--

--

The /dev/null of my life.

Love podcasts or audiobooks? Learn on the go with our new app.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
LoopedNetwork

LoopedNetwork

The /dev/null of my life.

More from Medium

How to setup SSH certificates for SSH authentication

What is Podman?

How to set up an SFTP server on Rocky Linux 8

5 Benefits of Vendor Neutral