Now that we’ve got our head around how to work with SOQL to pull data sets out of our Salesforce org, it’s time to circle back to loops.
In a previous post, we walked through While and Do-While Loops (and we learned to make Pavlova, which I seem to be obsessed with these days). I mentioned that there was another type of loop; a For loop. That’s the topic of today’s blog post; a fantastically flavor-filled foray into For loops.
I can’t wait to show you how this works. We are getting so close to a full-on trigger that I can almost taste it (salty and tangy … like really good salt and vinegar chips. Yum!).
Let’s do a quick recap on what a loop does …
What is a Loop?
At their most basic level, loops are pretty simple. They allow us to do something over and over again.
In Apex, there are three basic types of loops: While, Do-While, and For loops
Both While and Do-While loops take a condition, and loop through a piece of code until that condition is no longer true. For more information of While/Do-While loops, check out my previous blog post, Mix until combined (or using While and Do-While loops in Apex & Java).
But what makes For loops so special?
- For loops have a more compact syntax than While and Do-While loops.
- You can use For loops to iterate over a list or set
- You can use For loops with a SOQL. (Oooohhhh!)
In fact, there are three different For loops for each of these situations.
Let’s take a look …
For Loop Type #1: The Traditional For Loops
A traditional For loop is similar to both While and Do-While loops, but with a much more compact syntax. Referring to the Salesforce Force.com Apex Code Developer’s Guide, the basic syntax of a traditional For loop is:
for (initializeYourLoopVariable; loopsExitCondition; howToIncrementLoopVariable){ Code to loop over }
A simple example of this is:
for (Integer i = 0; i < 3; i++) { System.debug(i+1); }
Let’s take a closer look this the first line. Here’s what we’re doing:
- We have created (declared) a variable “i” as the Integer data type and given it a value of “0”:
Integer i = 0
- Then we’ve said that as soon as the following is no longer true, then stop looping:
i < 3
- Then we’ve said that at the end of each loop, we will increment i by 1. That’s what the Post-Increment Operator (e.g., “
++
“) is doing:i++
This basically gives us three iterations of the For loop. We are testing this, by outputting a line to our debug log (using System.debug) during each loop:
System.debug(i+1);
Run the entire code snippet (go on … it’s totally satisfying!) and you’ll see the following output in your debug log:
But how does this compare to our While loop example from my previous While loop post. Let’s take a look!
Comparison of Traditional For Loop and While Loop
You may remember that in my While loop post, we were baking a Pavlova (aren’t we always?). Part of that process is adding a cup of sugar to our beaten egg white, a tablespoon at a time.
// Create an integer variable representing how many portions we want to break our cup of sugar int Integer portionsOfSugar = 16; // Start the loop, while the number of sugar portions are greater than zero, then add sugar. While (portionsOfSugar > 0){ // Everything in the curly braces is what will repeat (or "loop"). In this case a method that will run the method that adds sugar, and then decrease the Portions of Sugar variable by one. addSugar(); portionsOfSugar--; };
We’re doing it by starting with an integer, “portionsOfSugar” and setting it to 16 (that’s how many tablespoons there are in a cup). With each pass through the loop, we are reducing that integer by one, using a Post-Decrement Operator (“--
“).
We can achieve the same thing with a traditional For Loop, but with a slightly different (more concise … and I think better) syntax.
// Create an integer variable (i) representing the number of tablespoons of sugar we want to add. Keep looping until we have no more sugar to add For (Integer i = 16; i > 0; i--){ // Everything in the curly braces is what will repeat (or "loop"). In this case a method that will run the method that adds sugar. If you need to use the addSugar(); }
Both pieces of code do the same thing, but I think the For loop has a simple elegance that is missing from the While version. But this is really a coding style thing. It’s good to know both (so when you run across it in someone else’s code, you don’t freak out … nah … you’d never do that!), but ultimately you’ll probably pick a style and go with it. (No style police here!)
I like using For loops, because not only can you do a traditional For loop to loop over a condition, but you can also use For loops to loop over a list or set and combine this with using data collected from a SOQL query (ooohhh, now that’s cool!).
For Loop Type #2: For Loop using a List or Set
First let’s create our list….
List<String> myFruit = new List<String>{'Pear', 'Apple', 'Banana', 'Orange'};
Note, this is a little different from how we populated our Lists in a previous post on Lists, Sets and Maps (also known as Apex Collections). Again, you’re in the swing of things now, so I’m introducing some coding shorthand. Either way is fine, but I like to type less than more … go figure!
Once, you’ve created the list or set, the basic structure of this kind of For loop is:
for (someVariableType SomeVariableName : someListOrSetToLoopOver) { Do something here, which can include using the variable that we passed in (e.g., someVariable). }
Let’s try this out with our collection of fruit, and with each loop we’ll add an entry into our debug log:
for (String s : myFruit) { System.debug('Fruit in my list: ' + s); }
Running the list and loop code together results in the following entries in our Debug Log:
I think this is pretty darn rad, but let’s make it more dynamic by combining this with a SOQL Query. If you’re new to SOQL, I recommend checking out my three part series on SOQL Queries.
For Loop Type #3: For Looping over a SOQL Query
I’m really excited now because this is the first time we’ve combined the super power of SOQL with the awesome power of Apex.
What we’re attempting here (do you see me rolling up my virtual sleeves and tying my apron strings?) is to:
- Create a list variable from a SOQL query
- Loop over that list and change the data within the list
- Use Database Manipulation Language (DML – see a previous post on working with sObjects) to post the updated data back to our Salesforce object!
Can you see why I’m excited?
So imagine if you will, the following scenario: We have added quite a few recipes to our custom Recipe__c object. But, oooops, we realize that we want to add a new field because we’re going to be asking for our readers’ recipe submissions and we want to record to whom the recipe is attributed! No problem, we add the lookup relationship field (attributedTo__c) that ties back to the Contact object. But we now have all these recipes that have no attribution! Right now, they should all be attributed to me (how convenient!).
As admins, we know that we export all the records, change them in Excel and reimport them, but I wanted to show you what this would look like in code (and show you how it’s actually less work to do it in Apex/SOQL). Nice!
First, let’s see what it would look like in pseudo code:
// Step 1: Create a list of recipes using a SOQL query // Step 2: Loop through the list and set the attributedTo__c field to my Contact ID // Step 3: Send this data as a batch up to Salesforce and update the recipe__c records!
The great thing about starting with pseudo code is that we already have the beginning of our code comments. Code comments are awesome, because, believe me, they make our future-developer-self ve-e-e-ry happy. The worst thing in the world (like … yah … really bad!) is to work hard on some code, and then months later have to make a change, and you have no idea what your code means. It’s so much easier to comment as you go and save yourself some headaches.
I’ve beefed up our comments so that we can step through our code bit by bit.
// Step 1: Create a list called "recipes" and feed it all the records in our recipe__c custom object, including the recipe ID, name and attributedTo__c fields. List<recipe__c> recipes = [SELECT id, name, attributedTo__c FROM recipe__c]; // Step 2: Create a variable, "r", of the recipe__c sObject data type, and Loop through the list, and for every record (sObject). For(recipe__c r: recipes){ // Then update each entry in the "recipes" list so that the r.attributedTo__c field is set to be my Contact ID. r.attributedTo__c = '003j000000JAxax'; // Then write a line to our debug log to document what we did. system.debug('Recipe: '+ r.name); } // Step 3: Use DML to send this data, as a single batch, through the "recipes" list, up to Salesforce and update the records in the recipe__c custom object. update recipes;
Curious why we put the DML statement outside our For Loop? We’ll talk about that, and another Apex best practice later on.
Decoding the For Loop
As we know from the first list loop code sample, the basic syntax for using a For loop over a list is:
for (someVariableType SomeVariableName : someListOrSetToLoopOver) { Do something here, again and again. }
We also saw above that we can loop over a list of strings (the fruit loop … get it!), so it’s hopefully not too much of a stretch to loop over a list of sObjects (in our case recipe__c records).
For(recipe__c r: recipes){ //do something here }
Let’s compare the two list loops:
Loop Element | Fruit-Loop Example | Recipe-Loop Example |
---|---|---|
Name of the list to loop over | myFruit | recipes |
Name of the variable used in the loop | s | r |
Data type of variable to be used in the loop | String | recipe__c |
As you can hopefully see…they are pretty much the same just that one deals with a list of strings and the other deals with a list of sObjects (in our case, records within recipe__c).
What about the stuff we put inside the loop … what does that do?
Code | What it’s doing |
---|---|
r.attributedTo__c = ‘003j000000JAxax’; | This sets the attributedTo__c field of the record we’re working with to my Contact ID (e.g., 003j000000JAxax). Note that we referenced the attributedTo__c field by using the variable name (“r”) and dot notation. |
system.debug(‘Recipe: ‘+ r.name); | This writes an entry to the debug log which recipe we just changed. |
A short commercial break for Apex Best Practices
We interrupt our program for a Public Service Announcement about Apex Best Practices.
I would be remiss if I didn’t talk about three Apex/SOQL Best Practices.
Best Practices 1 & 2 – Don’t put DML and SOQL inside a loop
As was memorialized in the most excellent Apex Best Practices: 15 Apex Commandments by James Loghry and Kevin Poorman, “Thou Shalt not put DML [or SOQL Queries] in loops.”
Why? Because you’ll soon come up against the infamous Salesforce Governor Limits <cue spooky music>.
Not only is making multiple SOQL queries and DML statements inefficient (aka slow), but it’s also one of those “be a good neighbor” things. When we all share one environment (like a series of connected very, very large houses), we don’t want to take up more resources than is necessary. To enforce good neighborly behavior, Salesforce limits us to 100 SOQL queries and 150 DML statements within a single transaction.
If you are looping through many times (think one loop per record) then you’ll soon hit your governor limits and Salesforce will throw you some wonderful errors:
For more information, check out Running Apex within Governor Execution Limits (another Salesforce Documentation Gem)
Best Practice #3 – Optimize Your SOQL for Loop for large data sets
First, a big #ThankYou goes out to the awesome Nate Lipke @EvilN8 and the Women Code Hero extraordinaire, Laura Baalman @lurlab for helping me wrap my head around this! I love living a #foreverLearning kind of life!
The Potential Problem: When you’re working with SOQL, and your query returns a very large data set, you may run up against the heap size limit (don’t worry about what this is right now, just know that its b-a-a-d and will throw an error).
One Solution: To fix this, we can change up our SOQL For loop syntax and use the super compact version:
for (variable_list : [soql_query]) { code_block }
Since we want to make some changes to the data within our list, then we actually need to throw in a second (inner) loop in there to get that working check out this final code:
//First, create a compact SOQL for loop, by creating list variable (called recipes) to store recipe__c sObjects. Populate the list with the records from a SOQL Query. for(List <recipe__c> recipes: [SELECT id, name, attributedTo__c FROM recipe__c]) { //Next, create a second for List loop that loops over the recipes list and updates the records within it. for(recipe__c r : recipes) { r.attributedTo__c = '003j000000JAxax'; system.debug('Recipe: '+ r.name); } //Lastly, use DML to send the updated recipes list up Salesforce and update the records in our recipe__c custom object. update recipes; }
But does’t this break rule number 1? Don’t put DML inside a for loop? Kind of, but not really. The reason you shouldn’t put DML inside a For loop is that you only want to run it once per transaction. In the example above, we are only executing the DML once, because we are doing all the transaction looping (where we change the value within the list) in an inner loop, and the DML is in the outer loop (which only runs once).
You can see why we didn’t start with this (a bit confusing … I know!), but I wanted to at least alert you about some potential problems, and show you an example of how to work around them. Also, this syntax has the added benefit of being super compact (which you know I’m partial to). You can find out more about this at two great Salesforce Developer Docs: SOQL For Loops and Working With Very Large SOQL Queries.
And don’t worry if you don’t get this … it’s not exactly the easiest thing to get. And you can always use the first way I showed to work with SOQL and loops until you get your feet under you.
Now back to our regularly scheduled program, “Cooking with Code!”
Running our Code
Ok, so theoretically this all works, but the proof, as they say, is in the pudding. Time to open the oven and reveal our finished dessert. Let’s see if it works!
Perhaps you’ve been following along, and also have a recipe__c custom object with the same fields I have (who wouldn’t!), and if so, you have all the data and the tools you need to run this kind of code.
- Log into your Developer Salesforce Org (please do NOT touch your production org … whew!) and open your Developer Console by clicking on your name and choosing “Developer Console.“
- Open the Execute Anonymous window: Debug > Open Execute Anonymous Window
- Type in the code and click the “Execute” button (make sure the “Open Log” box is also checked).
- Cross your fingers … and voila … we just updated all the recipes in our custom Object!
Note that you get a lot of data along with your system.debug lines, so if you just want to look at them, check the “Debug Only” box at the bottom of your Execution Log.
How cool is that!!! Pretty darn cool I say! Let’s take a look at my contact record, and see what happened there … yep, all the recipes are now attributed to me!
Summary For Loop Table
We’ve used For loops in three different ways within this post, so I wanted to pull it all together in a table that compares the three approaches, and talks about when they are particularly helpful.
For Loop Type | Unique usefulness |
---|---|
Traditional | Use this when you want to loop a specific number of times and/or have access to the iterator value (e.g., the value of “i”) from within the loop. |
With a Set or List | Use this any time you are handling data from a method* that returns a list or set. For example, imagine you have want your users to be able to create a shopping list from the ingredients within a recipe. Each ingredient has a checkbox next to it, that when checked, adds that ingredient to the users shoppingList set**. Then we could use a For loop to iterate over this set to display the contents. of their shopping list. |
With a SOQL query | Use this any time you want to loop over a set of records pulled from your Salesforce database. |
* Reminder that Methods are things that an object can do. For example, in the OOP Overview (How Buying a Major Appliance Can Help You Understand Object-Oriented Programming), we talked about an oven having methods like: setOvenTemp() and openOvenDoor(). Some Methods just do something and that’s it, and others do something and return a variable value. That variable can be of any type, including a set or list.
** See our post on Amuse-bouche of Apex Collections (Lists, Sets, and Maps) for more information.
Wrapping up our For Loop Package
We did a lot today! We combined different Force.com programming concepts from a bunch of different posts, including: Loops, SOQL, Collections, and DML. You should feel pretty proud (I know I am)!
We learned that just as While and Do-While loops can loop over a collection, so can For loops, but with a slightly different (and more compact) format.
We also learned a new way to create a list, and how to populate it with both static values that we defined and dynamic values using SOQL. Then we learned how to loop through this list using a For Loop to update the values within the list, and then use DML to send the list up to Salesforce to update our Salesforce Object.
We also threw in some Best Practices to keep you out of trouble (or at least the error message kind of trouble <wink>).
Bam! That’s what I call a good day’s work! Next time we’re going to take things a little further, and … dare I say it … write a trigger (woot woot!).
You must be logged in to post a comment.