Oh no. Oh geez. This is bad. Or at least extremely very not good. As a resident coding monkey, you’ve been in the process of building one of those fancy Lightning Web Components for Generic Company when, suddenly, you were struck with a sudden case of dumb, lost one too many games of foosball, and realized you’d accidently written a triple for loop.

You blame Karl.

The Delicious Context

You close your eyes, take a deep breath, and imagine the scenario once more. Generic Company is run by people who, like most humans, thoroughly enjoy food. They order mass amounts of food so regularly in fact that they resorted to Salesforce for order organization. An Order bundles Order Lines which hold whom Generic Company is ordering the food from via the related Account and the kind of food they’re ordering (American, Italian, Japanese, etc.) via the related Cuisine.

Furthermore, to ensnare Generic Company’s business, restaurants offer discounts depending on the order size! Each Order Line has several Discounts Tiers divided by price range, and the Order Line price gets discounted based on the ranges it falls into. If the $0-$100 range gets 0% off, $101-200 gets 5% off, and Generic Company was paying $150, the last $50 dollars gets discounted for a final total of $147.50.

It’s like tax brackets. I’m sorry I brought up tax brackets.

Bulk Food keeps track of the order line price by summing the cost of every Food Item (hamburgers, hot dogs, squid, etc.) being ordered. Calculating the discounted total price is the reason for your triple loop.

The Triple Issue and Time Complexity

You need to display discounted subtotals, grouped by Cuisine, grouped by Account. Surely a triple loop makes sense? You need to construct a multi-dimensional array for the HTML, and how could you calculate discounted prices from the Bulk Food record otherwise? If you explain it to your project manager, there’s no way he’ll say no to your current solution.

              “There’s no way I’ll say yes to your current solution,” said your Project Manager while drinking his fourth pint of decaf coffee.

              “Ok, but what if—”

              “Look at this graph.”

Pulling a large poster from behind his back with “Time Complexity” in bold at the top, you get flashbacks to your sophomore year Algorithms class.

listForHTML;

calcDiscountedPriceWithTriple() {
    let accToBulkFoodList; // map from Account Id to list of Bulk Food records
    let bulkFoodToTierList; // map from Bulk Food Id to list of Discount Tier records

    /** Imagine we assigned those maps with an Apex callout here please and thank you */
    
    for (let accId in accToBulkFoodList) { // runs n times

        let bulkList = [];
        for (let bulkId in accToBulkFoodList[accId]) { // runs n * n times (bad)

            for (let tier of bulkFoodToTierList[bulkId]) { // runs n * n * n times (bad and stupid and dare I say dumb)
                tier.discountedSubTotal; // calc subtotal and assign it to this new variable
            }
            let bulkObj = { bulkId: bulkId, tiers: bulkFoodToTierList[bulkId] };
            bulkList.push(bulkObj);
        }
        let accObj = { accId: accId, bulkRecords: bulkList };
        this.listForHTML.push(accObj);
    }
}

              “Do you remember Big-O notation?” he asked, turning back to you.

              “Yes. Where did you—”

He then proceeded to recite a seven-minute speech, reminding you how inner loops run for each element they’re iterating over times the number of outer loop iterations, meaning your triple loop wasn’t just bad, it was bad in an extra-special, exponential way. He also managed to work in a story about how his dog would run exponentially faster the more treats he offered.

              “Just use an inner class to help remove the inner loops,” he announced as he wrapped up his discourse.

Taking a moment to process everything, you gaze at the ceiling. Two minutes pass. You finish your contemplation and look at your project manager.

              “How long did you have that poster in your pocket?”

You stare into each other’s eyes.

              “I don’t have any pockets.”

Inner Classes

While normal Apex classes, called top-level or outer classes, are collections of variables and functions, inner classes, on the other hand, are collections of variables and functions. The unique placement of inner class definitions, inside outer classes, helps group classes that belong together without creating another file, and you can make instances of these inner classes from any outer class! If you were incredibly creative and defined the outer class “OuterClass” with the inner class “InnerClass,” you could instantiate InnerClass with “InnerClass varName” if you were in OuterClass. From another outer class, you could instantiate InnerClass via dot notation with “OuterClass.InnerClass varName.”

public class OuterClass {
    
    public static void makeInnerObject() {
        InnerClass testObject = new InnerClass();
        testObject.quantity = '2000';

        System.debug(testObject); // Will reveal 2,000 cheeseburgers
    }

    class InnerClass {
        String name;
        Integer quantity;

        public InnerClass() {
            name = 'Cheeseburger';
        }
    }
}
public class OtherClass {

    public static void makeInnerObjectFromElsewhere() {
        OuterClass.InnerClass testObject = new OuterClass.InnerClass();
        testObject.quantity = '3';

        System.debug(testObject); // Will reveal a meager 3 cheeseburgers
    }
}

Now that I’ve said “inner” and “outer” more than I ever have before, let me explain why inner classes are nifty. You can make inner classes to help you easily deserialize JSON. You can make inner classes to help you do the exact opposite of that (serialize JSON). You can define inner classes with different sharing rules than its outer class to better encapsulate data. Or you could simply use inner classes to package data in a single object which is really the only thing that matters for the triple loop.

Loop Reduction with Inner Classes

You snap back to reality, filled with determination as the retreating footsteps of your project manager don’t echo at all because the floor is carpeted. You’re going to group data ahead of time to avoid you triple loop.

But what to group? Well, you want the positions of your constructed array elements to be based on Account, Cuisine, and Discount, so if those elements innately had that information, you wouldn’t need to loop over each attribute separately; you could loop over all elements in one go, calculate the subtotal for each discount, then organize the elements into your multi-dimensional array.

listForHTML;

calcDiscountedPriceWithSingle() {
    let tierInfoList; // inner object list from Apex with tier list fields, Account Id, Bulk Food cost, etc.

    /** Imagine we assigned the list with an Apex callout here please and thank you */

    // indexTracker keeps track of the account's and bulk food's indices so we can
    // easily push tiers to the correct inner list
    let indexTracker = new Map(); // map from account Id to object with list index and indices for each related bulk food
    let i = 0; // current index in listForHTML 
    for (let tierInfo of tierInfoList) {

        tierInfo.discountedSubTotal; // be epic and calc subtotal here

        // Imagine a really cool if statement that's too long for this blog post 
        // where we check if the related account or bulk food already exists in listForHTML using our indexTracker, 
        // and we create a new entry in the list or push to the list depending

        index++;
    }
}

Before you start creating inner classes that contain the sum of human knowledge, know that memory is not infinite. Sure, you could place every field in your org within an inner class, but you could also data load Moby Dick to get similar performance results. Programming is a perpetual war between time and memory. Choose programming techniques with those resources in mind.

Conclusion

Inner classes are, dare I say, pretty neat. To repeat everything I covered in a much more concise manner:

  • You can define classes within classes called inner classes.
  • Inner classes can help with:
    • Grouping classes without making another file.
    • JSON serialization/deserialization.
    • Generalization (grouping variables and functions).
  • Grouping outer loop data with their inner loop data can remove the need for that inner loop.
  • Balance performance and memory when deciding whether to reduce a loop.

And the moral of the story is to not use inner loops when you don’t have to because that would be dumb.

The end.

Ready to get started?

It's time for you to get the Salesforce that works for YOU. Let us help.

© Upsource Solutions, LLC. All rights reserved. Site by Stimulus.