Category Archives: “Advanced Apex Programming” by Dan Appleman

Checking on the Children: Which loop to execute?

I’ve been reading Advanced Apex Programming for Salesforce.com(R) and Force.com(R) by Dan Appleman.  If you’re an Apex programmer, or are aspiring to be one, I highly recommend buying a copy.  I’ve already learned a lot from it, and I’m only in Chapter 4 of 11.

On pages 80 – 82, Appleman introduces a concept that I decided to look at with some code of my own.  He says, “One question that you’ll often face is choosing which object to loop through.”  In this case, Appleman was talking about a method to find Opportunity records without child Task records.  I decided to use custom objects and write a method to check for child objects to make sure I understood his concept.  I went with a concept familiar to me from work on systems I deal with daily: Invoices and Invoice Line Items.

PLEASE NOTE:   The following code is based on Appleman’s samples in Chapter 4 of Advanced Apex Programming…, though mine is far less elegant, and uses custom objects instead of CRM standard objects.


public with sharing class InvoiceIdeas {

 public static Set<Invoice__c> findInvoicesWithNoLineItems(List<Invoice__c> invoicesToCheck) {
 
 Map<Id, Invoice__c> invoiceMap = new Map<Id, Invoice__c>(invoicesToCheck);
 
 Set<Id> invoiceIds = new Set<Id>();
 for(Invoice__c i : invoicesToCheck) {
 System.debug('AN INVOICE ID ADDED...');
 invoiceIds.add(i.Id);
 }
 
 // In terms of worst-case analyis for bulkification, let's say an invoice might have as 
 // many as 30 line items, time 200 possible invoices passed in, for 6000 total records.
 List<Invoice_Line_Item__c> allInvoiceLineItems = [ SELECT Name, Invoice__c FROM Invoice_Line_Item__c
 WHERE Invoice__c = :invoiceIds ];
 
 Set<Invoice__c> invoicesWithoutLineItems = new Set<Invoice__c>();
 for(Invoice__c i : invoicesToCheck) {
 invoicesWithoutLineItems.add(i);
 }
 
 Integer loopIterations = 0;
 
 // Option 1: Loop through all the Invoice records, and see if they have any line items.
 /*
 for(Invoice__c inv : invoicesToCheck) {
 for(Invoice_Line_Item__c lineItem : allInvoiceLineItems) {
 if(inv.Id == lineItem.Invoice__c) invoicesWithoutLineItems.remove(inv);
 loopIterations++;
 }
 }
 */
 
 // Option 2: Loop through the Invoice_Line_Item__c list, and remove their parent Invoice__c
 // record's ID from the set invoicesWithoutLineItems.
 
 for(Invoice_Line_Item__c lineItem : allInvoiceLineItems) {
 Invoice__c inv = invoiceMap.get(lineItem.Invoice__c);
 invoicesWithoutLineItems.remove(inv);
 loopIterations++;
 }
 
 
 System.debug('NUMBER OF LOOP ITERATIONS: ' + loopIterations);
 for(Invoice__c i : invoicesWithoutLineItems) {
 System.debug('Invoice without line item: ' + i.Id);
 }
 
 return invoicesWithoutLineItems;
 
 }

}

I then created 3 invoices in my developer org; one with no line items, and two more with three line items each.

Next, I went into the Execute Anonymous window in the Force.com IDE plugin for Eclipse and ran the following:

List<Invoice__c> myInvList = [ SELECT Id, Name FROM Invoice__c ];

InvoiceIdeas.findInvoicesWithNoLineItems(myInvList);

When I use “Option 1” above, I get 18 iterations of the inner loop. When I use “Option 2” above, I get only 6 iterations of the single loop. So, back in the day when Salesforce was limiting our number of script statements, I can see how our choice of which object to loop through was extremely important. These days, the governor limit we, as developers, have to worry about around this and similar patterns is the CPU time limit. In this case, I put a call to this method (with the Option 1 code running) in a for loop that ran 50 times, and I still didn’t get anything but 0 registering as the CPU time. However, it stands to reason that in a different context with more work being done in the loop(s), this pattern would save us a significant amount of CPU time.