Written by Randle Haggerty
On August 1, 2022

Salesforce Flow Basics Pt. 3: Loop, Collection Sort, and Collection Filters

Jonathan_SalesforceFlowBasics

About the Author:

Jonathan Davis is a Salesforce Consultant at Venn Technology in Grapevine, TX. He enjoys the challenge of mastering all things Salesforce. With seven certifications under his belt and counting, he’s always tackling Salesforce Trailhead to up his integration and configuration skills.

 

In our previous article, we talked about the flow resources Variables, Collections, and Formulas.

In this article we will be talking about the flow elements used to interact with and modify Collection Variables: Loops, Collection Sorts, and Collection Filters. These elements are very powerful tools that allow flows to work with large numbers of records while respecting the limits that Salesforce enforces.

 

Jump to a section:

Loop Element
Collection Sort
Collection Filters
Tips and Tricks

 

Loop Element

Without loops, there is only so much a flow can do to reference, modify, or create multiple records without hitting limits.

Although the Update Element does have the ability to modify more than one record by setting the filter criteria broad enough to find more than one record, you are only able to make the same change to all of the records that meet that criteria. Without loops, in order to update different records with different values based on logic, you need to create a number of Update Elements each with different criteria. Doing this can quickly cause you to hit flow limit errors with large datasets since Salesforce limits the number of times you can use the Data Elements (Get, Update, Create, Delete) to interact with the database.

The best practice is to reduce the number of Data Elements as much as possible to avoid hitting limits and improving org performance. Loops help you do this.

When a loop is created, you must select a Collection Variable for it to loop through. Here we are creating a loop using a collection of Contact Records retrieved from a Get Records element earlier in the flow:

Select Collection Variable

Once created, we can add other flow elements inside the flow after the “For Each” and before the “After Last” labels:

Flow Elements - Loop

Each element placed inside the flow will be run once for each item in the flow’s collection, allowing you to reference and modify them.

Elements inside the flow have the ability to reference a special variable called “Current Item from Loop (Loop_Name)” that holds the current item being processed by the loop. So, if a collection contains the number values 1, 2, and 47, the current item from the Loop Variable would be 1 the first time the loop processed, 2 the second time, and 47 the third time. Meaning, all of the flow logic within the loop will run based on the current value. 

In our Contact example, if we needed to update the business phone for any active contacts to match the business phone on the Account, we would first create a Decision Element inside the loop to check if the current contact being processed has a specific checkbox marked as FALSE (indicating that the contacts are active):

Loop - Decision Element

This creates two branching paths inside of the loop where more elements can be placed:

Loops in Salesforce Flows

Then, we’ll use an Assignment Element to update the active contacts traveling through the flow with the new phone number value from the Account that started the flow:

Assignment Element Loops

Assignment - Update Phone Number

We then create a new Collection Variable with the data type of Record and Object type of Contact named ContactsToUpdate. Then use a second Assignment Element to add the current contact traveling through the loop into this ContactsToUpdate Collection Variable:

Loop - Collection Variable

Assignment - Add to Update Collection

Next, create an Update Records element after the flow is complete to update the ContactsToUpdate Collection.

Update Records - Collection

Update Records - Update Contracts

Summing up what we did here, when the loop runs, each contact in the original collection is checked to make sure they are not inactive. The inactive contacts follow the default path where no changes are made, while the active contacts have their Phone field updated, and are placed into the collection for updates to be made after the loop processes the last item in the original collection.

Although it is true that this update could also have been done with an Update Element filtering on the Inactive field, this example illustrates the way logic inside loops can be used to update various records based on different logic. 

If we also needed to change the email of a subset of the active contacts, this would not be possible in a single Update Records element, and would require a second decision in the loop to make that change as well:

Update Records - Update Email

In this example, we change the phone number of all active contacts but may only change the email on a subset of them before putting them all in the same collection to be updated. This means that when the final ContactsToUpdate collection is updated, it could contain some contacts that had only their phone number changed and others that had both their phone and emails changed, which all get updated together in the same database element.

Loops can also be used when many records need to be created based on other records. For example, if you had a business requirement to create a Sales Invoice (a custom object) when an Opportunity was closed won, and needed each of the Opportunity Line Items to create a corresponding Sales Invoice Line Item (a child object to the Sales Invoice object), a loop would help to achieve this. 

The basic steps to this process would be:

  1. Trigger the flow off the Opportunity as an after save flow when the Stage of the Opportunity changes to closed won. 
  2. Create the corresponding Sales Invoice record with a Create Record element using values from the triggering Opportunity record. 
  3. Use a Get Records element to retrieve all of the Opportunity Product records associated with the Opportunity. 
  4. Use a decision to confirm that Opportunity Products were found. 
  5. Create a single Record type variable of object type Sales Invoice Line Item
  6. Create a Collection Record type variable of object type Sales Invoice Line Item
  7. Loop through the Opportunity Products from the Get Records element
  8. Inside the loop, use an Assignment element to assign the values for each line item to the Sales Invoice Line Item single record variable. Make sure to relate these lines to the Id of the Sales Invoice in the previous Create Records element. 
  9. Inside the loop, use a second Assignment element to add the newly populated Sales Invoice Line Item Record variable into a Sales Invoice Line Item Record Collection variable.
  10. After the last item in the loop, use a Create Records element to create the Sales Invoice Line Items in the Sales Invoice Line Item Record Collection variable.

Here is what this flow would look like in the builder:

Creating more records using Loops

Loops can also be used inside other loops, usually to compare the values of two collections in some way against each other.

If for example, you had a collection of 10 Sales Invoice Line Items and another collection of 10 Opportunity Products that each referenced 10 product values, and you wanted to know the number of products they had in common and send various emails based on the result, you could do this by following these steps:

  • Use a Get Records element to retrieve the Opportunity Product records. 
  • Use a Get Records element to retrieve the Sales Invoice Line Items records. 
  • Use a Decision element to confirm that records were found.
  • Use a Loop element to loop through the Opportunity Product records. 
  • Inside the Loop, use another Loop element to loop through the Sales Invoice Line Items.
  • Inside the second loop, use a Decision element to determine if the current items from each loop both have the same value in their individual product fields.
    • If the products match, use an Assignment variable to add 1 to a number variable that counts the number of matching products. 
    • If the products do not match, continue on with the flow. 
  • Outside of all of the loops, create a decision based on the number of matches. In the below example, we send different emails based on the number of matches. 

 Here is what this flow would look like in the builder:

Using Loops to compare Collections

Double loops mean that when the first loop runs, the entire second loop runs for each item in the first loop. 

One thing to be aware of with these types of flows is that depending on the number of records being compared, it can be easy to hit the built in limits of flows. In our above example, if you have 10 Opportunity Products in the first collection, and 10 Sales Invoice Line Items in the second collection, the second loop would run 100 times as each of the 10 Opportunity Line Items are compared against each of the 10 Sales Invoice Line Items. 

Flows have a built in limit of 2000 flow elements per flow, and any violation of this limit will result in an error for the user running the flow and a rollback of any actions the flow took when it ran. This means that if we had more than around 44 records in each collection, we would quickly start hitting these limits.

If you need to compare large numbers of records against each other there are a number of ways around this problem. One way to reduce the number of flow elements in our above example would be to leave the second loop after a product match is found instead of processing the remaining Sales Invoices Line Items. This is done by changing the flow connection after the Assignment element from the second loop to the first loop:

Loops - Compare large number of records

Assignment - Increase Counter Variable

This change results in fewer flow elements each time the flow runs, because if the match is made on the first comparison between an Opportunity Product and Sales Invoice Line Item, the flow does not check the remaining Sales Invoice Line Items and skips to the next Opportunity Product. Note that this method is useful when there are a lot of matches in the collections, but collections with few matches will still use a higher number of flow elements. 

If there is a requirement to manipulate the number of flow elements even more than this, Collection Sort and Collection Filter elements can also be used.

 

Collection Filter

The Collection Filter is used to create a new collection variable that contains a filtered subset of an original collection’s contents using the values of the items or fields in that collection. 

When the element is configured, select the collection you wish to filter, and set the conditions items must meet in order to be copied over to the new collection based on conditions or a formula evaluation as True. The new collection created by the filter can then be referenced like any other collection in the flow. 

In this example, we create a Collection Filter element to filter a collection of Sales Invoice Line Items to only include records with a value in their End Date fields:

Collection Filter Element

When a formula is used to filter the collection, the items in the original collection are referenced by a special variable created by the Collection Filter called “currentItemFromSourceCollection,” where the contents of a Sales Invoice Line Item collection are filtered to contain only items that have the same product as the current item in a loop of opportunity products.

Collection Filters are very powerful when it comes to reducing the number of unnecessary elements from the flow by situationally reducing collections to only contain the values needed. In the example from the previous section where two large collections are being compared, a collection filter element can be used between the two loops. This will filter the Sales Invoice Line Item in the second loop by the product value on the Opportunity Product record being looped through:

Loops - Opportunity Product Record

This filtering happens each time the first loop runs, drastically narrowing down the records in the second collection, and making the loop much more efficient. 

With this change, the overall flow looks like this:

Loops and Collections - Flow Builder

Now, the second loop circles the filtered collection rather than the full Sales Invoice Line Item collection. With these changes, if there are 10 Opportunity Products and 10 Sales Invoice Line Items, each time the first loop runs the collection used in the second loop is reduced to contain only items that match the product on the Opportunity being processed by the first loop, which (assuming there are no duplicates in the Sales Invoice Line Item list) will always be 1 or 0 zero items. This means the flow will use fewer elements than the previous versions since we are only looping the exact records we need to, allowing us to process hundreds of records in the two collections.

 

Collection Sort

The Collection Sort element is used to sort the contents of a collection by ascending or descending order, and optionally reduce the number of items in that collection to a specified number. 

The Collection Sort element sorts record collection variables based on a field value in those records. For collection variables containing other data types, the Collection Sort element sorts based on the value itself. 

Here is an example of a collection sort element configuration that would return the 5 Sales Invoice Line Items with the earliest “End_Date__c” field:

Collection Sort Element

Collection Sort is an element that is not used often in comparison to others available in flows, but can be very useful in certain situations. The main reason why it’s not used often is because the Get Records element also allows you to sort the contents of a record collection it returns, and tends to be the most common source of collections in general. That said, there are situations when you need to return only the highest or lowest value from a collection, or want to process a collection of items in a particular order then the Collection Sort is the most efficient solution using the lowest number of elements and interactions with the database.

One thing to be aware of is that If the collection variable is reduced to one1 item, it is still a collection variable containing one item rather than a single variable, so you would need to use a Loop and Assignment element to assign the single item in the sorted collection to a single record variable in order to access that value elsewhere in the flow.

 

Tips and Tricks

 

Using loops to de-duplicate collections

Loops can be used to sort through a collection that contains duplicate values and return a unique list for various business requirements. If there was a need to provide a list of opportunity products a custom had purchased, loops could be used to iterate through all of the opportunity products on an Opportunity and add the unique values to a new collection.

The steps to this process would be:

  1. Use a Get Records element to retrieve the Opportunity Products.
  2. Use a Decision element to confirm Opportunity Product records have been found. 
  3. Create a text type variable to hold the Id of the Products in the list. 
  4. Create an Opportunity Product record type collection variable to hold the de-duplicated list of products. 
  5. Loop through the Opportunity Products collection from the Get Records element. 
  6. Inside the loop, create a decision element to check if the text variable you created contains the Id of the product in the current Opportunity Product item in the loop. If the Product Id is in the text variable, the flow continues to the next item in the loop. If the Product Id is not in the text variable (the default path), then the flow continues to the next Assignment element. 
  7. Create an Assignment element on the default path to add the Product Id from the current Opportunity Product to the text variable. Also add the current Opportunity Product item to the Opportunity Product record type collection variable created in step 4.

The result of this flow is that all of the unique product records are in the collection variable, which can then be used elsewhere in the flow.

 

Using loops to create text variables

Flow loops can be used to create text variables that are built from the contents of collections. These text variables can then be used elsewhere in the flow in places such as Email Templates or Screen elements in screen flows. 

In the previous example, if there was a need to email the unique Product Code instead of needing a unique list of records, a loop could be used to create a formatted text variable that contains only the codes of the products.

 

The steps for this would be:

  1. Use a Get Records element to retrieve the Opportunity Products
  2. Use a Decision element to confirm Opportunity Product records have been found. 
  3. Create a text type variable to hold the list of Product Code. 
  4. Create a text type formula to construct the text value to put into the text variable for each item in the list. This formula would be something like:
    1. {CurrentItemFromCollection.Product2.ProductCode}&”, ”
  5. Loop through the Opportunity Products collection from the Get Records element. 
  6. Inside the loop, create a decision element to check if the text variable you created contains the code of the product in the current Opportunity Product item in the loop. If the Product Code is in the text variable, the flow continues to the next item in the loop. If the Product Code is not in the text variable (the default path), then the flow continues to the next Assignment element. 
  7. Create an Assignment element on the default path to add the text from the formula into the text variable.
  8. Create another text type formula to display the final result of the Product Code list. This formula should trim the extra comma and space from the last item in the list with LEFT() and LEN() as shown below:
    1. LEFT({!ProductList}, LEN({!ProductList}) -2)

For more information on Salesforce Flows, check out our next blog post: Flow Element Basics – Screen, Pause, Action, and Subflow Elements

 

 
 
Sign up for Venn Sync Newsletter