<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Flow on Uwe Braig</title><link>https://blog.uwe-braig.de/tags/flow/</link><description>Recent content in Flow on Uwe Braig</description><generator>Hugo -- gohugo.io</generator><language>en</language><lastBuildDate>Fri, 24 Oct 2025 00:00:00 +0000</lastBuildDate><atom:link href="https://blog.uwe-braig.de/tags/flow/index.xml" rel="self" type="application/rss+xml"/><item><title>How to Build a Complex Validation Rule for Multi-Select Picklist in Salesforce Flow</title><link>https://blog.uwe-braig.de/how-to-build-a-complex-validation-rule-for-multi-select-picklist-in-salesforce-flow/</link><pubDate>Mon, 14 Jul 2025 00:00:00 +0000</pubDate><guid>https://blog.uwe-braig.de/how-to-build-a-complex-validation-rule-for-multi-select-picklist-in-salesforce-flow/</guid><description>&lt;p&gt;Multi-Select Picklists can be tricky to handle when it comes to validations. In this article I will show how a Flow with the &amp;ldquo;Custom Error Message&amp;rdquo; element can be used for a validation logic that can be way more complex than with the &amp;ldquo;classic&amp;rdquo; validation rules.&lt;/p&gt;
&lt;p&gt;The example requirement is to create a validation message upon record save for a multi-select picklist field &amp;ldquo;Product Interest&amp;rdquo; as follows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We want to allow users to choose additional values, e.g. add &amp;ldquo;Dairy Products — Butter&amp;rdquo; to the &amp;ldquo;chosen&amp;rdquo; list.&lt;/li&gt;
&lt;li&gt;But we want to prevent users from un-selecting values, e.g. remove &amp;ldquo;Fruits — Apples&amp;rdquo; from the &amp;ldquo;chosen&amp;rdquo; list.&lt;/li&gt;
&lt;/ul&gt;
&lt;figure&gt;&lt;img src="https://blog.uwe-braig.de/how-to-build-a-complex-validation-rule-for-multi-select-picklist-in-salesforce-flow/01-picklist-field.png"
			alt="Multi-select picklist field Product Interest"&gt;&lt;figcaption&gt;
			&lt;p&gt;Multi-select picklist field &amp;ldquo;Product Interest&amp;rdquo;.&lt;/p&gt;
		&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id="the-data-model"&gt;The Data Model
&lt;/h2&gt;&lt;p&gt;The example will be shown with a multi-select picklist custom field on the Lead object:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;API Name: ProductInterest__c
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Label: Product Interest
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Data Type: Picklist (Multi-Select)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Global Value Set: Product Groups
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;The global value set &amp;ldquo;Product Groups&amp;rdquo;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Values API Name
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;------------------------- ---------------
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Dairy Products - Butter Dairy.03
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Dairy Products - Cheese Dairy.04
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Dairy Products - Milk Dairy.01
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Dairy Products - Yogurt Dairy.02
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Fruits - Apples Fruits.01
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Fruits - Bananas Fruits.02
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Fruits - Oranges Fruits.04
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Fruits - Strawberries Fruits.03
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Sweets - Chocolate bars Sweets.01
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Sweets - Cookies Sweets.02
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Vegetables - Broccoli Vegetables.02
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Vegetables - Carrots Vegetables.01
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Vegetables - Spinach Vegetables.03
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id="the-flow"&gt;The Flow
&lt;/h2&gt;&lt;p&gt;See below the preview of the flow we are about to build:&lt;/p&gt;
&lt;figure&gt;&lt;img src="https://blog.uwe-braig.de/how-to-build-a-complex-validation-rule-for-multi-select-picklist-in-salesforce-flow/02-flow-overview.png"
			alt="The Validation Flow"&gt;&lt;figcaption&gt;
			&lt;p&gt;The Validation Flow&lt;/p&gt;
		&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h3 id="start-element"&gt;Start Element
&lt;/h3&gt;&lt;p&gt;Create a &amp;ldquo;Record-Triggered Flow&amp;rdquo; that runs before the record is saved. (Will also work with an after-save flow if this better fits your use case.)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Object: &lt;strong&gt;Lead&lt;/strong&gt; (or any other standard or custom object)&lt;/li&gt;
&lt;li&gt;Trigger the Flow When: &amp;ldquo;&lt;strong&gt;A record is updated&lt;/strong&gt;&amp;rdquo;&lt;/li&gt;
&lt;li&gt;Condition Requirements: &lt;strong&gt;Field &amp;ldquo;Product Interest&amp;rdquo; Is Changed = True&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;figure&gt;&lt;img src="https://blog.uwe-braig.de/how-to-build-a-complex-validation-rule-for-multi-select-picklist-in-salesforce-flow/03-start-element.png"
			alt="Start Element configuration"&gt;&lt;figcaption&gt;
			&lt;p&gt;Start Element configuration&lt;/p&gt;
		&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h3 id="helper-constant"&gt;Helper Constant
&lt;/h3&gt;&lt;p&gt;Define a Constant as follows for clearer reference inside the Flow:&lt;/p&gt;
&lt;figure&gt;&lt;img src="https://blog.uwe-braig.de/how-to-build-a-complex-validation-rule-for-multi-select-picklist-in-salesforce-flow/04-constants.png"
			alt="Section Constants"&gt;&lt;figcaption&gt;
			&lt;p&gt;Section &amp;ldquo;Constants&amp;rdquo;&lt;/p&gt;
		&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure&gt;&lt;img src="https://blog.uwe-braig.de/how-to-build-a-complex-validation-rule-for-multi-select-picklist-in-salesforce-flow/05-constant-delimiter.png"
			alt="Define the semicolon as a constant PicklistValueDelimiter"&gt;&lt;figcaption&gt;
			&lt;p&gt;Define the semicolon &amp;ldquo;;&amp;rdquo; as a constant PicklistValueDelimiter&lt;/p&gt;
		&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h3 id="helper-variables"&gt;Helper Variables
&lt;/h3&gt;&lt;p&gt;We will need two helper text variables:&lt;/p&gt;
&lt;figure&gt;&lt;img src="https://blog.uwe-braig.de/how-to-build-a-complex-validation-rule-for-multi-select-picklist-in-salesforce-flow/06-variables.png"
			alt="Section Variables"&gt;&lt;figcaption&gt;
			&lt;p&gt;Section &amp;ldquo;Variables&amp;rdquo;&lt;/p&gt;
		&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;defined as follows:&lt;/p&gt;
&lt;figure&gt;&lt;img src="https://blog.uwe-braig.de/how-to-build-a-complex-validation-rule-for-multi-select-picklist-in-salesforce-flow/07-variable-current.png"
			alt="Text Variable: CurrentPicklistValues"&gt;&lt;figcaption&gt;
			&lt;p&gt;Text Variable: CurrentPicklistValues&lt;/p&gt;
		&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure&gt;&lt;img src="https://blog.uwe-braig.de/how-to-build-a-complex-validation-rule-for-multi-select-picklist-in-salesforce-flow/08-variable-prior.png"
			alt="Text Variable: PriorPicklistValues"&gt;&lt;figcaption&gt;
			&lt;p&gt;Text Variable: PriorPicklistValues&lt;/p&gt;
		&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h3 id="get-the-object-definition"&gt;Get the Object Definition
&lt;/h3&gt;&lt;p&gt;A &lt;strong&gt;Get Records&lt;/strong&gt; element to get the object &amp;ldquo;&lt;strong&gt;Entity Definition&lt;/strong&gt;&amp;rdquo; with the definition of the object we are working on, here: Lead.&lt;/p&gt;
&lt;figure&gt;&lt;img src="https://blog.uwe-braig.de/how-to-build-a-complex-validation-rule-for-multi-select-picklist-in-salesforce-flow/09-get-object-definition.png"
			alt="getObjectDefinition"&gt;&lt;figcaption&gt;
			&lt;p&gt;getObjectDefinition&lt;/p&gt;
		&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;&lt;em&gt;If you want to do this with a custom object, don&amp;rsquo;t forget to add &amp;ldquo;__c&amp;rdquo; to the API Name, e.g. &amp;ldquo;MyCustomObject__c&amp;rdquo;.&lt;/em&gt;&lt;/p&gt;
&lt;h3 id="get-the-field-definition"&gt;Get the Field Definition
&lt;/h3&gt;&lt;p&gt;A &lt;strong&gt;Get Records&lt;/strong&gt; element to query the object &amp;ldquo;&lt;strong&gt;Field Definition&lt;/strong&gt;&amp;rdquo; for our multi-select picklist field &amp;ldquo;&lt;strong&gt;ProductInterest__c&lt;/strong&gt;&amp;rdquo; using the &amp;ldquo;Durable Id&amp;rdquo; we&amp;rsquo;ve got in the previous step:&lt;/p&gt;
&lt;figure&gt;&lt;img src="https://blog.uwe-braig.de/how-to-build-a-complex-validation-rule-for-multi-select-picklist-in-salesforce-flow/10-get-field-definition.png"
			alt="getFieldDefinition"&gt;&lt;figcaption&gt;
			&lt;p&gt;getFieldDefinition&lt;/p&gt;
		&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h3 id="get-the-active-picklist-values"&gt;Get the Active Picklist Values
&lt;/h3&gt;&lt;p&gt;A &lt;strong&gt;Get Records&lt;/strong&gt; element to query the object &amp;ldquo;&lt;strong&gt;Picklist Value Info&lt;/strong&gt;&amp;rdquo; for all currently active picklist values for this field:&lt;/p&gt;
&lt;figure&gt;&lt;img src="https://blog.uwe-braig.de/how-to-build-a-complex-validation-rule-for-multi-select-picklist-in-salesforce-flow/11-get-picklist-values.png"
			alt="getActivePicklistValues"&gt;&lt;figcaption&gt;
			&lt;p&gt;getActivePicklistValues&lt;/p&gt;
		&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Notes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Make sure the setting under &amp;ldquo;How Many Records to Store&amp;rdquo; reads &amp;ldquo;&lt;strong&gt;All records&lt;/strong&gt;&amp;rdquo; (unlike the other Get Records elements before).&lt;/li&gt;
&lt;li&gt;This will only return active picklist values. There is no way to query for inactive values with this method.&lt;/li&gt;
&lt;li&gt;In case you are using Record Types, also be aware: This will only return picklist values for the current record type of the current record.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="convert-the-picklist-field-values-to-delimiter-separated-text-variables"&gt;Convert the Picklist Field Values to Delimiter-separated Text Variables
&lt;/h3&gt;&lt;p&gt;An &lt;strong&gt;Assignment&lt;/strong&gt; Element as follows:&lt;/p&gt;
&lt;figure&gt;&lt;img src="https://blog.uwe-braig.de/how-to-build-a-complex-validation-rule-for-multi-select-picklist-in-salesforce-flow/12-assignment-convert.png"
			alt="convertPicklistToText"&gt;&lt;figcaption&gt;
			&lt;p&gt;convertPicklistToText&lt;/p&gt;
		&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;En detail, this reads:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{!CurrentPicklistValues} Equals {!$Record.ProductInterest__c}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{!PriorPicklistValues} Equals {!$Record__Prior.ProductInterest__c}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{!CurrentPicklistValues} Add {!PicklistValueDelimiter}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{!PriorPicklistValues} Add {!PicklistValueDelimiter}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="loop-through-the-active-picklist-values"&gt;Loop Through the Active Picklist Values
&lt;/h3&gt;&lt;p&gt;Now we loop through all the values as per record collection returned by getActivePicklistValues:&lt;/p&gt;
&lt;figure&gt;&lt;img src="https://blog.uwe-braig.de/how-to-build-a-complex-validation-rule-for-multi-select-picklist-in-salesforce-flow/13-loop.png"
			alt="loopPicklistValues"&gt;&lt;figcaption&gt;
			&lt;p&gt;loopPicklistValues&lt;/p&gt;
		&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;For each known picklist value, we check whether it was present in the PRIOR value but is missing in the CURRENT value. If such case occurs, we know that we need to raise the validation error message towards the user:&lt;/p&gt;
&lt;figure&gt;&lt;img src="https://blog.uwe-braig.de/how-to-build-a-complex-validation-rule-for-multi-select-picklist-in-salesforce-flow/14-check-removed.png"
			alt="Check Has it been removed"&gt;&lt;figcaption&gt;
			&lt;p&gt;Check_Has_it_been_removed&lt;/p&gt;
		&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;The condition logic reads &amp;ldquo;&lt;strong&gt;1 AND NOT 2&lt;/strong&gt;&amp;rdquo; with the following conditions:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{!PriorPicklistValues} Contains {!LoopPicklistValueWithTrailingDelimiter}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{!CurrentPicklistValues} Contains {!LoopPicklistValueWithTrailingDelimiter}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;where &amp;ldquo;LoopPicklistValueWithTrailingDelimiter&amp;rdquo; is a helper text formula defined as follows:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{!loopPicklistValues.Value} + {!PicklistValueDelimiter}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="validation-error-message"&gt;Validation Error Message
&lt;/h3&gt;&lt;p&gt;Upon the first occurrence of such case, we use the &amp;ldquo;&lt;strong&gt;Custom Error Message&lt;/strong&gt;&amp;rdquo; element to raise the validation error to the user and stop the flow:&lt;/p&gt;
&lt;figure&gt;&lt;img src="https://blog.uwe-braig.de/how-to-build-a-complex-validation-rule-for-multi-select-picklist-in-salesforce-flow/15-validation-message.png"
			alt="ValidationMessage"&gt;&lt;figcaption&gt;
			&lt;p&gt;ValidationMessage&lt;/p&gt;
		&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id="show-case"&gt;Show Case
&lt;/h2&gt;&lt;p&gt;Now let&amp;rsquo;s run a test: Go to the example Lead, click &amp;ldquo;Edit&amp;rdquo; and play around with the multi-select picklist fields. Once you try to save the record after un-choosing a picklist value, the validation error message will be shown:&lt;/p&gt;
&lt;figure&gt;&lt;img src="https://blog.uwe-braig.de/how-to-build-a-complex-validation-rule-for-multi-select-picklist-in-salesforce-flow/16-test-run.png"
			alt="Test Run"&gt;&lt;figcaption&gt;
			&lt;p&gt;Test Run&lt;/p&gt;
		&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id="conclusion-and-considerations"&gt;Conclusion and Considerations
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;The &amp;ldquo;Custom Error Message&amp;rdquo; flow element has the same effect as a traditional validation rule: Once fired, the save transaction will fail and the user (in case of a UI transaction) or the caller of the interface (in case of an API/Dataloader transaction) will get the custom error message.&lt;/li&gt;
&lt;li&gt;Record-triggered flows are way more flexible for implementing complex validation logic compared to the formula field-style &amp;ldquo;classic&amp;rdquo; validation rules.&lt;/li&gt;
&lt;li&gt;Always be aware of the &amp;ldquo;order of execution&amp;rdquo;: In our example, we&amp;rsquo;ve used a before-save flow as this gives best performance in such scenario and we know that, by our design, no other logic will touch the picklist values on our record. But you need to assess whether other parts of other flows might update the picklist fields and you need to put the validation elsewhere in the order of execution, e.g. by setting &amp;ldquo;Trigger Order&amp;rdquo; in the flow properties and/or switching to an after-save flow.&lt;/li&gt;
&lt;li&gt;The &amp;ldquo;Get Records&amp;rdquo; on &amp;ldquo;Picklist Value Info&amp;rdquo; only ever will see and return active picklist values for the given record type. Hence, we cannot identify the (very special) case where the user decides to un-choose a value that is a now-inactive picklist value: We don&amp;rsquo;t know about it, hence we won&amp;rsquo;t check for it in our loop.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;This article has been written as per Summer &amp;lsquo;25 release.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;For more information about handling picklist values in Flow also see the article&lt;/em&gt; &lt;a class="link" href="https://blog.uwe-braig.de/how-to-get-picklist-labels-in-salesforce-flow" &gt;&lt;em&gt;&amp;ldquo;How to get Picklist Labels in Salesforce Flow&amp;rdquo;&lt;/em&gt;&lt;/a&gt;&lt;em&gt;.&lt;/em&gt;&lt;/p&gt;</description></item><item><title>How to Concatenate a Collection of Strings to a Comma-Delimited String in Salesforce Flow</title><link>https://blog.uwe-braig.de/how-to-concatenate-a-collection-of-strings-to-a-comma-delimited-string-in-salesforce-flow/</link><pubDate>Mon, 23 Sep 2024 00:00:00 +0000</pubDate><guid>https://blog.uwe-braig.de/how-to-concatenate-a-collection-of-strings-to-a-comma-delimited-string-in-salesforce-flow/</guid><description>&lt;p&gt;Salesforce Flow has come a long way and there have been vast improvements over the years and still there are interesting challenges when it comes to working with collections. But sometimes Flow is way more powerful than expected if you are willing to dig out a not-so-obvious solution.&lt;/p&gt;
&lt;p&gt;The challenge here is: How to get from a list/collection of strings/records to a single comma-separated (.csv) formatted string?&lt;/p&gt;
&lt;figure&gt;&lt;img src="https://blog.uwe-braig.de/how-to-concatenate-a-collection-of-strings-to-a-comma-delimited-string-in-salesforce-flow/01-csv-challenge.png"
			alt="The .csv Challenge"&gt;&lt;figcaption&gt;
			&lt;p&gt;The .csv Challenge&lt;/p&gt;
		&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;In this post I will present various options and discuss the pros and cons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The Basic Approach:&lt;/strong&gt; a very simple one with just one flow formula needed to convert a text collection into a comma-separated string.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The Basic Approach with custom delimiters:&lt;/strong&gt; an extended version of the first approach that is a bit more flexible, but still fits into a single flow formula.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The Full Approach — with a text collection as the source:&lt;/strong&gt; a fully featured approach that covers all special cases w.r.t. escape sequences and delimiter configuration, but requires more steps than just a single flow formula.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The Full Approach — with a record collection as the source:&lt;/strong&gt; same approach, slightly different steps to use the Transform element instead of a Loop.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All options shown are contained in a sample demo Screen Flow that can be found here: &lt;code&gt;CSV_Concatenation_Screen_Flow.flow-meta.xml&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Now let&amp;rsquo;s see, what we can get out of example data as shown below:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Payload&lt;/span&gt; &lt;span class="c1"&gt;#1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Payload&lt;/span&gt; &lt;span class="c1"&gt;#2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Payload&lt;/span&gt; &lt;span class="c1"&gt;#3 with &amp;#34;1&amp;#34;,&amp;#34;2&amp;#34;,&amp;#34;3&amp;#34; commas&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Payload&lt;/span&gt; &lt;span class="c1"&gt;#4 with &amp;#34;string&amp;#34; delims&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Payload&lt;/span&gt; &lt;span class="c1"&gt;#5 with &amp;#34;;&amp;#34; semicolon delim&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id="the-basic-approach"&gt;The Basic Approach
&lt;/h2&gt;&lt;p&gt;We take a flow variable of type string collection as starting point and populate it with some test values.&lt;/p&gt;
&lt;figure&gt;&lt;img src="https://blog.uwe-braig.de/how-to-concatenate-a-collection-of-strings-to-a-comma-delimited-string-in-salesforce-flow/02-mytextcollection-variable.png"
			alt="MyTextCollection flow variable"&gt;&lt;figcaption&gt;
			&lt;p&gt;MyTextCollection flow variable.&lt;/p&gt;
		&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure&gt;&lt;img src="https://blog.uwe-braig.de/how-to-concatenate-a-collection-of-strings-to-a-comma-delimited-string-in-salesforce-flow/03-populate-text-collection.png"
			alt="Populate text collection with the demo values"&gt;&lt;figcaption&gt;
			&lt;p&gt;Populate text collection with the demo values.&lt;/p&gt;
		&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;To convert this string collection to a comma-separated string, we just have to use it in a flow formula and do some simple post-processing to remove the &lt;code&gt;[&lt;/code&gt; at the beginning and the &lt;code&gt;]&lt;/code&gt; at the end that the formula puts there by default. See flow text formula &lt;code&gt;toCsvBasicApproach&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/* Cut off leading/trailing [ and ] */
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;MID({!MyTextCollection}, 2, LEN({!MyTextCollection}) - 2)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;What we get is:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Payload&lt;/span&gt; &lt;span class="c1"&gt;#1,Payload #2,Payload #3 with &amp;#34;1&amp;#34;,&amp;#34;2&amp;#34;,&amp;#34;3&amp;#34; commas,Payload #4 with &amp;#34;string&amp;#34; delims,Payload #5 with &amp;#34;;&amp;#34; semicolon delim&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="considerations"&gt;Considerations
&lt;/h3&gt;&lt;p&gt;This is quick and simple and works very well, if, and only if, the following constraints are ok for your use case:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Payload SHOULD NOT contain &lt;code&gt;&amp;quot;&lt;/code&gt; because this might or might not confuse the parser of the consumer, depending on its exact implementation. However, we should generally consider this as .csv non-best practice. Hence, do not use if the payload may contain &lt;code&gt;&amp;quot;&lt;/code&gt; characters.&lt;/li&gt;
&lt;li&gt;Payload MUST NOT contain any comma &lt;code&gt;,&lt;/code&gt; because with this approach the formulas cannot distinguish payload comma from field delimiting comma, so the whole result string will be broken once any payload string contains even a single comma.&lt;/li&gt;
&lt;li&gt;Field delimiter is fixed to comma &lt;code&gt;,&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Generally no string delimiter.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Or, to put it simple, this will work great for converting e.g.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Text 1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Text 2
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Text 3
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;into &lt;code&gt;Text 1,Text 2,Text 3&lt;/code&gt;&lt;/p&gt;
&lt;h2 id="the-basic-approach-with-custom-delimiters"&gt;The Basic Approach with Custom Delimiters
&lt;/h2&gt;&lt;p&gt;For this improved approach, we want to have control over field delimiter and string delimiter. So we define two text variables, &lt;code&gt;FieldDelimiter&lt;/code&gt; and &lt;code&gt;StringDelimiter&lt;/code&gt; and initialize accordingly:&lt;/p&gt;
&lt;figure&gt;&lt;img src="https://blog.uwe-braig.de/how-to-concatenate-a-collection-of-strings-to-a-comma-delimited-string-in-salesforce-flow/04-delimiter-assignment.png"
			alt="Assign desired field delimiter and string delimiters"&gt;&lt;figcaption&gt;
			&lt;p&gt;Assign desired field delimiter and string delimiters.&lt;/p&gt;
		&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;And we now use an enhanced formula &lt;code&gt;toCsvBasicWithDelimitersApproach&lt;/code&gt; to convert our text collection:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{!StringDelimiter}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/* leading string delimiter */
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;+ SUBSTITUTE(
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/* outer replace: comma field delimiter to target field delimiter plus string delimiter */
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; SUBSTITUTE(
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; /* inner replace: pre-processing to escape string delimiters by duplicating it */
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; {!ToCsvBasicApproach},
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; {!StringDelimiter},
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; {!StringDelimiter} + {!StringDelimiter}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ),
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;,&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; {!StringDelimiter} + {!FieldDelimiter} + {!StringDelimiter}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;+ {!StringDelimiter}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/* trailing string delimiter */
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;What we get:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;Payload #1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Payload #2&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Payload #3 with &amp;#34;&amp;#34;1&amp;#34;&amp;#34;,&amp;#34;&amp;#34;2&amp;#34;&amp;#34;,&amp;#34;&amp;#34;3&amp;#34;&amp;#34; commas&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Payload #4 with &amp;#34;&amp;#34;string&amp;#34;&amp;#34; delims&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Payload #5 with &amp;#34;&amp;#34;;&amp;#34;&amp;#34; semicolon delim&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="considerations-1"&gt;Considerations
&lt;/h3&gt;&lt;p&gt;This delivers some minor improvements:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We can configure field delimiter and string delimiter.&lt;/li&gt;
&lt;li&gt;We get the string delimiter properly escaped if contained in payload.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;However, we still cannot deal with &lt;code&gt;,&lt;/code&gt; comma as part of the payload: The replace operation still cannot distinguish between the comma created by concatenation and the comma that comes as part of the payload.&lt;/p&gt;
&lt;p&gt;Again, to put it simple, works well for&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Text 1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Text 2
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Text &amp;#34;Special Case&amp;#34; 3
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;to be converted into &lt;code&gt;&amp;quot;Text 1&amp;quot;;&amp;quot;Text 2&amp;quot;;&amp;quot;Text &amp;quot;&amp;quot;Special Case&amp;quot;&amp;quot; 3&amp;quot;&lt;/code&gt;&lt;/p&gt;
&lt;h2 id="the-full-approach"&gt;The Full Approach
&lt;/h2&gt;&lt;p&gt;To further improve, we need to pre-process the payload strings first before doing the concatenation. The pre-processing allows to handle literally all combinations of string delimiter and field delimiter also being part of the payload.&lt;/p&gt;
&lt;p&gt;There are two options, depending on where the string collection originates from:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Case A:&lt;/strong&gt; To pre-process a text collection, we put a Loop around our text collection with a single assignment element inside the loop. The actual work is done within a formula field again.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Case B:&lt;/strong&gt; If the payload data happens to originate from the text field of a record collection, it is easier to combine two Transform elements to do the trick without the need for a loop. (N.B.: This will use an enhancement of the Transform element available in Winter &amp;lsquo;25.)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="case-a-text-collection-as-the-source"&gt;Case A: Text Collection as the Source
&lt;/h3&gt;&lt;p&gt;In total, the steps in the flow are:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Put together the text collection as in the previous examples.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Loop&lt;/strong&gt; through the text collection with one assignment element inside the loop for a formula to do the pre-processing.&lt;/li&gt;
&lt;li&gt;Access the resulting text collection via &lt;strong&gt;Formula&lt;/strong&gt; that does the post-processing into a single .csv formatted string.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The Loop will take the initial text collection…&lt;/p&gt;
&lt;figure&gt;&lt;img src="https://blog.uwe-braig.de/how-to-concatenate-a-collection-of-strings-to-a-comma-delimited-string-in-salesforce-flow/05-loop-element.png"
			alt="Loop through text collection"&gt;&lt;figcaption&gt;
			&lt;p&gt;Loop through text collection&lt;/p&gt;
		&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;… and access each source text item via the formula &lt;code&gt;toCsvPreprocessedLoopItem&lt;/code&gt; that does the pre-processing…&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;StringDelimiter&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;/*&lt;/span&gt; &lt;span class="n"&gt;each&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="n"&gt;with&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="n"&gt;delimiter&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="o"&gt;*/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;SUBSTITUTE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SUBSTITUTE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;SUBSTITUTE&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;toCsvPreprocessorLoop&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;StringDelimiter&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;StringDelimiter&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;StringDelimiter&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;/*&lt;/span&gt; &lt;span class="n"&gt;escape&lt;/span&gt; &lt;span class="n"&gt;StringDelimiter&lt;/span&gt; &lt;span class="n"&gt;inside&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="n"&gt;by&lt;/span&gt; &lt;span class="n"&gt;duplicating&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="n"&gt;everywhere&lt;/span&gt; &lt;span class="o"&gt;*/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;%&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;URLENCODE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;%&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;/*&lt;/span&gt; &lt;span class="n"&gt;first&lt;/span&gt; &lt;span class="n"&gt;urlencode&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="nb"&gt;sign&lt;/span&gt; &lt;span class="n"&gt;inside&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;*/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;,&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;URLENCODE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;,&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;/*&lt;/span&gt; &lt;span class="n"&gt;then&lt;/span&gt; &lt;span class="n"&gt;urlencode&lt;/span&gt; &lt;span class="n"&gt;standard&lt;/span&gt; &lt;span class="n"&gt;comma&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="n"&gt;delimiter&lt;/span&gt; &lt;span class="n"&gt;inside&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;*/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;StringDelimiter&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;/*&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;also&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="n"&gt;with&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="n"&gt;delimiter&lt;/span&gt; &lt;span class="o"&gt;*/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;…and collects it into a new text collection named &lt;code&gt;PreprocessedTextCollection&lt;/code&gt;:&lt;/p&gt;
&lt;figure&gt;&lt;img src="https://blog.uwe-braig.de/how-to-concatenate-a-collection-of-strings-to-a-comma-delimited-string-in-salesforce-flow/06-assignment-inside-loop.png"
			alt="Assignment element inside the loop"&gt;&lt;figcaption&gt;
			&lt;p&gt;Assignment element inside the loop&lt;/p&gt;
		&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;All items in the text collection now are pre-processed as follows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;All string delimiters are escaped by duplicating them, e.g. &lt;code&gt;&amp;quot;&lt;/code&gt; to &lt;code&gt;&amp;quot;&amp;quot;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;All &lt;code&gt;%&lt;/code&gt; characters inside the payload are URL-encoded to &lt;code&gt;%25&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;All comma &lt;code&gt;,&lt;/code&gt; characters are URL-encoded to &lt;code&gt;%2C&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Whole payload enclosed in string delimiters, e.g. &lt;code&gt;&amp;quot;payload&amp;quot;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now we can access this pre-processed text collection via the formula &lt;code&gt;toCsvPreprocessorApproach&lt;/code&gt;…&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;SUBSTITUTE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SUBSTITUTE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;SUBSTITUTE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;MID&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;PreprocessedTextCollection&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LEN&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;PreprocessedTextCollection&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;/*&lt;/span&gt; &lt;span class="n"&gt;strip&lt;/span&gt; &lt;span class="n"&gt;off&lt;/span&gt; &lt;span class="n"&gt;starting&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;leading&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;StringDelimiter&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;,&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;StringDelimiter&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;/*&lt;/span&gt; &lt;span class="n"&gt;replace&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="n"&gt;delimiter&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="o"&gt;*/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;StringDelimiter&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;FieldDelimiter&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;StringDelimiter&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;/*&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="n"&gt;by&lt;/span&gt; &lt;span class="n"&gt;our&lt;/span&gt; &lt;span class="n"&gt;own&lt;/span&gt; &lt;span class="n"&gt;one&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="n"&gt;Note&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="n"&gt;delimiter&lt;/span&gt; &lt;span class="n"&gt;inside&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="n"&gt;is&lt;/span&gt; &lt;span class="n"&gt;URLencoded&lt;/span&gt; &lt;span class="o"&gt;*/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;URLENCODE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;,&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;,&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;/*&lt;/span&gt; &lt;span class="n"&gt;revert&lt;/span&gt; &lt;span class="n"&gt;urlencoding&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;comma&lt;/span&gt; &lt;span class="n"&gt;inside&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;*/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;URLENCODE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;%&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;%&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;/*&lt;/span&gt; &lt;span class="n"&gt;revert&lt;/span&gt; &lt;span class="n"&gt;urlencoding&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;character&lt;/span&gt; &lt;span class="o"&gt;*/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;…to get this built-to-order and properly escaped .csv string:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;Payload #1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Payload #2&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Payload #3 with &amp;#34;&amp;#34;1&amp;#34;&amp;#34;,&amp;#34;&amp;#34;2&amp;#34;&amp;#34;,&amp;#34;&amp;#34;3&amp;#34;&amp;#34; commas&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Payload #4 with &amp;#34;&amp;#34;string&amp;#34;&amp;#34; delims&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Payload #5 with &amp;#34;&amp;#34;;&amp;#34;&amp;#34; semicolon delim&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="case-b-record-collection-as-the-source"&gt;Case B: Record Collection as the Source
&lt;/h3&gt;&lt;p&gt;If the source data comes from record collection anyway, we can avoid the loop by having the Transform element do the tricks.&lt;/p&gt;
&lt;p&gt;In total, the steps in the flow are:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Get Records&lt;/strong&gt; element to retrieve the Source Data, here: Accounts&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Transform&lt;/strong&gt; element #1 to pre-process the desired field, here: Account.Name&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Transform&lt;/strong&gt; element #2 to put all Account.Name fields into a simple text collection.&lt;/li&gt;
&lt;li&gt;Access the resulting text collection via &lt;strong&gt;Formula&lt;/strong&gt; that does the post-processing into a single .csv formatted string.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We start with getting the Account records in question via a Get Records element. For this example we assume, that it returns 4 Accounts with Account names as given in our example data above.&lt;/p&gt;
&lt;figure&gt;&lt;img src="https://blog.uwe-braig.de/how-to-concatenate-a-collection-of-strings-to-a-comma-delimited-string-in-salesforce-flow/07-get-records-element.png"
			alt="Get Records Element"&gt;&lt;figcaption&gt;
			&lt;p&gt;Get Records Element&lt;/p&gt;
		&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Then we use a Transform element that will take the Accounts collection from the previous step and will return another Accounts collection for temporary use. This Target Data collection will have the pre-processed version of Account name.&lt;/p&gt;
&lt;figure&gt;&lt;img src="https://blog.uwe-braig.de/how-to-concatenate-a-collection-of-strings-to-a-comma-delimited-string-in-salesforce-flow/08-transform-element-1.png"
			alt="Transform Element #1"&gt;&lt;figcaption&gt;
			&lt;p&gt;Transform Element #1&lt;/p&gt;
		&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;It will map &lt;code&gt;Account.Name&lt;/code&gt; from Source Data to &lt;code&gt;Account.Name&lt;/code&gt; in Target Data with the following formula for the pre-processing:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;StringDelimiter&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;/*&lt;/span&gt; &lt;span class="n"&gt;each&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="n"&gt;with&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="n"&gt;delimiter&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="o"&gt;*/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;SUBSTITUTE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SUBSTITUTE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;SUBSTITUTE&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;getAccounts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="n"&gt;EachItem&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;StringDelimiter&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;StringDelimiter&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;StringDelimiter&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;/*&lt;/span&gt; &lt;span class="n"&gt;escape&lt;/span&gt; &lt;span class="n"&gt;StringDelimiter&lt;/span&gt; &lt;span class="n"&gt;inside&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="n"&gt;by&lt;/span&gt; &lt;span class="n"&gt;duplicating&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="n"&gt;everywhere&lt;/span&gt; &lt;span class="o"&gt;*/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;%&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;URLENCODE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;%&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;/*&lt;/span&gt; &lt;span class="n"&gt;first&lt;/span&gt; &lt;span class="n"&gt;urlencode&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="nb"&gt;sign&lt;/span&gt; &lt;span class="n"&gt;inside&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;*/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;,&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;URLENCODE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;,&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;/*&lt;/span&gt; &lt;span class="n"&gt;then&lt;/span&gt; &lt;span class="n"&gt;urlencode&lt;/span&gt; &lt;span class="n"&gt;standard&lt;/span&gt; &lt;span class="n"&gt;comma&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="n"&gt;delimiter&lt;/span&gt; &lt;span class="n"&gt;inside&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;*/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;StringDelimiter&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;/*&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;also&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="n"&gt;with&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="n"&gt;delimiter&lt;/span&gt; &lt;span class="o"&gt;*/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;We get our Account names pre-processed as follows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;All string delimiters are escaped by duplicating them, e.g. &lt;code&gt;&amp;quot;&lt;/code&gt; to &lt;code&gt;&amp;quot;&amp;quot;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;All &lt;code&gt;%&lt;/code&gt; characters inside the payload are URL-encoded to &lt;code&gt;%25&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;All comma &lt;code&gt;,&lt;/code&gt; characters are URL-encoded to &lt;code&gt;%2C&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Whole payload enclosed in string delimiters, e.g. &lt;code&gt;&amp;quot;payload&amp;quot;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With the next Transform element, we will take this temporary Accounts collection as Source Data and transform all &lt;code&gt;Account.Name&lt;/code&gt; to a simple text collection:&lt;/p&gt;
&lt;figure&gt;&lt;img src="https://blog.uwe-braig.de/how-to-concatenate-a-collection-of-strings-to-a-comma-delimited-string-in-salesforce-flow/09-transform-element-2.png"
			alt="Transform Element #2"&gt;&lt;figcaption&gt;
			&lt;p&gt;Transform Element #2&lt;/p&gt;
		&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Similar to the Basic Approach at the very beginning, we take this text collection as input for a formula field that does all the post-processing to deliver the final .csv formatted string:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;SUBSTITUTE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SUBSTITUTE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;SUBSTITUTE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;MID&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;PreprocessedTextCollection&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LEN&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;PreprocessedTextCollection&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;/*&lt;/span&gt; &lt;span class="n"&gt;strip&lt;/span&gt; &lt;span class="n"&gt;off&lt;/span&gt; &lt;span class="n"&gt;starting&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;leading&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;StringDelimiter&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;,&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;StringDelimiter&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;/*&lt;/span&gt; &lt;span class="n"&gt;replace&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="n"&gt;delimiter&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="o"&gt;*/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;StringDelimiter&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;FieldDelimiter&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;StringDelimiter&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;/*&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="n"&gt;by&lt;/span&gt; &lt;span class="n"&gt;our&lt;/span&gt; &lt;span class="n"&gt;own&lt;/span&gt; &lt;span class="n"&gt;one&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="n"&gt;Note&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="n"&gt;delimiter&lt;/span&gt; &lt;span class="n"&gt;inside&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="n"&gt;is&lt;/span&gt; &lt;span class="n"&gt;URLencoded&lt;/span&gt; &lt;span class="o"&gt;*/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;URLENCODE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;,&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;,&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;/*&lt;/span&gt; &lt;span class="n"&gt;revert&lt;/span&gt; &lt;span class="n"&gt;urlencoding&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;comma&lt;/span&gt; &lt;span class="n"&gt;inside&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;*/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;URLENCODE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;%&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;%&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;/*&lt;/span&gt; &lt;span class="n"&gt;revert&lt;/span&gt; &lt;span class="n"&gt;urlencoding&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;character&lt;/span&gt; &lt;span class="o"&gt;*/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Here we go — from this selection of Account names…&lt;/p&gt;
&lt;figure&gt;&lt;img src="https://blog.uwe-braig.de/how-to-concatenate-a-collection-of-strings-to-a-comma-delimited-string-in-salesforce-flow/10-accounts-collection.png"
			alt="Accounts Collection"&gt;&lt;figcaption&gt;
			&lt;p&gt;Accounts Collection&lt;/p&gt;
		&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;…we get this built-to-order and properly escaped .csv string:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;Payload #1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Payload #2&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Payload #3 with &amp;#34;&amp;#34;1&amp;#34;&amp;#34;,&amp;#34;&amp;#34;2&amp;#34;&amp;#34;,&amp;#34;&amp;#34;3&amp;#34;&amp;#34; commas&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Payload #4 with &amp;#34;&amp;#34;string&amp;#34;&amp;#34; delims&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Payload #5 with &amp;#34;&amp;#34;;&amp;#34;&amp;#34; semicolon delim&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id="considerations-2"&gt;Considerations
&lt;/h2&gt;&lt;p&gt;The &amp;ldquo;full&amp;rdquo; approach properly handles all potential field delimiters and string delimiters that might occur inside the payload. So it is supposed to be suitable irrespective of actual content of the payload.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;String delimiter and field delimiter are configurable.&lt;/li&gt;
&lt;li&gt;New Transform element as of Winter &amp;lsquo;25 simplifies pre-processing without the need for a Loop element in certain cases.&lt;/li&gt;
&lt;li&gt;In cases where we still rely on a Loop, we benefit from the fact, that the former limit &amp;ldquo;execution of maximum number of elements per flow interview = 2000&amp;rdquo; has been removed in Spring &amp;lsquo;23.&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>How to get Picklist Labels in Salesforce Flow</title><link>https://blog.uwe-braig.de/how-to-get-picklist-labels-in-salesforce-flow/</link><pubDate>Fri, 16 Aug 2024 00:00:00 +0000</pubDate><guid>https://blog.uwe-braig.de/how-to-get-picklist-labels-in-salesforce-flow/</guid><description>&lt;figure&gt;&lt;img src="https://blog.uwe-braig.de/how-to-get-picklist-labels-in-salesforce-flow/01-article-header.png"
			alt="How to get Picklist Labels in Salesforce Flow"&gt;
&lt;/figure&gt;

&lt;p&gt;When working with picklist fields in Salesforce Flows, e.g. in a formula or a text template, the flow can only &amp;ldquo;see&amp;rdquo; the picklist API value and not the picklist label as shown in the GUI. This might not be an issue in your org if API value and label are configured to be always same and translation workbench is not in use. In all other cases, this can create usability constraints, e.g. when it comes to showing picklist values in Screen Flows or sending e-mails from within the Flow.&lt;/p&gt;
&lt;p&gt;I will discuss the following solutions:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Use the Picklist screen component to extract the label.&lt;/li&gt;
&lt;li&gt;Use a set of Get Record elements to query the PicklistValueInfo standard object in order to find the label for a given picklist value.&lt;/li&gt;
&lt;li&gt;Use an invocable action in Flow to have an APEX class return the information from metadata.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;All options in this article focus on handling single records and getting labels to display information on the screen or use text templates, for example to put together e-mails to be sent by the flow.&lt;/p&gt;
&lt;p&gt;There are additional challenges when it comes to working with record collections, for example, to be used in the Data Table screen component. There might be a future article about this.&lt;/p&gt;
&lt;h2 id="the-basic-scenario"&gt;The Basic Scenario
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;A Salesforce org with the translation workbench enabled.&lt;/li&gt;
&lt;li&gt;Active languages are English, German and Spanish.&lt;/li&gt;
&lt;li&gt;Translations are in place for English and German only.&lt;/li&gt;
&lt;li&gt;A custom object &lt;code&gt;MyCustomObject__c&lt;/code&gt; and its corresponding custom tab.&lt;/li&gt;
&lt;li&gt;A custom picklist field &lt;code&gt;MyPicklist__c&lt;/code&gt; using the global value set &lt;code&gt;MyPicklistValueSet&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;This global value set and its translations are shown in the table below.&lt;/li&gt;
&lt;/ul&gt;
&lt;figure&gt;&lt;img src="https://blog.uwe-braig.de/how-to-get-picklist-labels-in-salesforce-flow/02-mypicklistvalueset.png"
			alt="MyPicklistValueSet"&gt;&lt;figcaption&gt;
			&lt;p&gt;MyPicklistValueSet&lt;/p&gt;
		&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Our example record looks like this:&lt;/p&gt;
&lt;figure&gt;&lt;img src="https://blog.uwe-braig.de/how-to-get-picklist-labels-in-salesforce-flow/03-example-record.png"
			alt="Example Record"&gt;&lt;figcaption&gt;
			&lt;p&gt;Example Record&lt;/p&gt;
		&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;We do a simple flow that gets the example record by its record ID and puts its fields on a screen component:&lt;/p&gt;
&lt;figure&gt;&lt;img src="https://blog.uwe-braig.de/how-to-get-picklist-labels-in-salesforce-flow/04-basic-scenario-flow.png"
			alt="Basic Scenario Flow"&gt;&lt;figcaption&gt;
			&lt;p&gt;Basic Scenario Flow&lt;/p&gt;
		&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;The screen component will show:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The Field component for the custom field &lt;code&gt;MyPicklist__c&lt;/code&gt; of our example record.&lt;/li&gt;
&lt;li&gt;A Display Text component that outputs the content of&lt;br&gt;
a) the picklist field directly,&lt;br&gt;
b) a text formula field referencing the picklist field,&lt;br&gt;
c) a text template referencing the picklist field.&lt;/li&gt;
&lt;/ol&gt;
&lt;figure&gt;&lt;img src="https://blog.uwe-braig.de/how-to-get-picklist-labels-in-salesforce-flow/05-field-component.png"
			alt="Field Component"&gt;&lt;figcaption&gt;
			&lt;p&gt;Field Component&lt;/p&gt;
		&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure&gt;&lt;img src="https://blog.uwe-braig.de/how-to-get-picklist-labels-in-salesforce-flow/06-display-text-component.png"
			alt="Display Text Component"&gt;&lt;figcaption&gt;
			&lt;p&gt;Display Text Component&lt;/p&gt;
		&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure&gt;&lt;img src="https://blog.uwe-braig.de/how-to-get-picklist-labels-in-salesforce-flow/07-text-formula.png"
			alt="Text Formula"&gt;&lt;figcaption&gt;
			&lt;p&gt;Text Formula&lt;/p&gt;
		&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure&gt;&lt;img src="https://blog.uwe-braig.de/how-to-get-picklist-labels-in-salesforce-flow/08-text-template.png"
			alt="Text Template"&gt;&lt;figcaption&gt;
			&lt;p&gt;Text Template&lt;/p&gt;
		&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;And here comes what is wrong with it when we run this flow: Although the field component properly renders the picklist field with its (translated) label as we would expect it, all further references to the picklist only show the API value.&lt;/p&gt;
&lt;figure&gt;&lt;img src="https://blog.uwe-braig.de/how-to-get-picklist-labels-in-salesforce-flow/09-flow-output-basic.png"
			alt="Flow Output"&gt;&lt;figcaption&gt;
			&lt;p&gt;Flow Output&lt;/p&gt;
		&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id="solution-1-picklist-choice-set"&gt;Solution #1: Picklist Choice Set
&lt;/h2&gt;&lt;p&gt;There is a difference between putting the &lt;em&gt;Field Component&lt;/em&gt; of the picklist custom field on a screen and putting a &lt;em&gt;Picklist Choice Set&lt;/em&gt; on a screen. As opposed to the basic scenario, here we use the Picklist Choice Set:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Add a new ressource of type Picklist Choice Set.&lt;/li&gt;
&lt;li&gt;Set Object to the custom object where our picklist field is located.&lt;/li&gt;
&lt;li&gt;Set the picklist field.&lt;/li&gt;
&lt;/ul&gt;
&lt;figure&gt;&lt;img src="https://blog.uwe-braig.de/how-to-get-picklist-labels-in-salesforce-flow/10-mypicklistchoiceset.png"
			alt="MyPicklistChoiceSet"&gt;&lt;figcaption&gt;
			&lt;p&gt;MyPicklistChoiceSet&lt;/p&gt;
		&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Now the first component in our screen will be a picklist component that will refer to above Picklist Choice Set:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Multiple Options = No&lt;/li&gt;
&lt;li&gt;Component Type = Picklist&lt;/li&gt;
&lt;li&gt;Choice = link to Picklist Choice Set as created above&lt;/li&gt;
&lt;li&gt;Default Value = The picklist field value from our example record.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Plus: Add the Picklist Component in the Display Text section.&lt;/p&gt;
&lt;figure&gt;&lt;img src="https://blog.uwe-braig.de/how-to-get-picklist-labels-in-salesforce-flow/11-picklist-component.png"
			alt="Picklist Component"&gt;&lt;figcaption&gt;
			&lt;p&gt;Picklist Component&lt;/p&gt;
		&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Our debug run will now look like this:&lt;/p&gt;
&lt;figure&gt;&lt;img src="https://blog.uwe-braig.de/how-to-get-picklist-labels-in-salesforce-flow/12-flow-output-solution1.png"
			alt="Flow Output"&gt;&lt;figcaption&gt;
			&lt;p&gt;Flow Output&lt;/p&gt;
		&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;The text output of the Picklist Component will also work in formulas and text templates.&lt;/p&gt;
&lt;p&gt;Considerations for this approach:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Obviously it will only work in screen flows, i.e. not suitable for record-triggered flows nor scheduled flows.&lt;/li&gt;
&lt;li&gt;The Picklist Component must be rendered and visited during the screen flow, i.e. it MUST be shown to the user. It could be set to read-only (Disabled = &lt;code&gt;{!$GlobalConstant.True}&lt;/code&gt;), but not to hidden. Otherwise it won&amp;rsquo;t be initialized with its default value and will be empty on accessing it. This might or might not be compatible with your specific use case.&lt;/li&gt;
&lt;li&gt;Not suitable for record collections, especially for showing picklist labels in the Data Table component.&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://help.salesforce.com/s/articleView?id=sf.flow_considerations_limit_transaction.htm&amp;amp;type=5" target="_blank" rel="noopener"
 &gt;Per-Transaction Flow Limits&lt;/a&gt;: Does not count towards &amp;ldquo;Total number of SOQL queries issued&amp;rdquo;.&lt;/li&gt;
&lt;li&gt;It will always deliver the label for the language of the running user, i.e. either the Master Label or the label as per translation workbench.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="solution-2-get-record-to-query-picklistvalueinfo"&gt;Solution #2: „Get Record&amp;quot; to Query PicklistValueInfo
&lt;/h2&gt;&lt;p&gt;In this approach, we will query the standard objects EntityDefinition, FieldDefinition and PicklistValueInfo with the &amp;ldquo;Get Record&amp;rdquo; element in order to get the picklist label:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Query for the object definition of our object &lt;code&gt;MyCustomObject__c&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Query for the field definition of our custom field &lt;code&gt;MyPicklist__c&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Query for the picklist value info (including the label) for the value in our example record.&lt;/li&gt;
&lt;/ol&gt;
&lt;figure&gt;&lt;img src="https://blog.uwe-braig.de/how-to-get-picklist-labels-in-salesforce-flow/13-flow-with-picklistvalueinfo.png"
			alt="Flow with PicklistValueInfo"&gt;&lt;figcaption&gt;
			&lt;p&gt;Flow with PicklistValueInfo&lt;/p&gt;
		&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Here the details for getObjectDefinition:&lt;/p&gt;
&lt;figure&gt;&lt;img src="https://blog.uwe-braig.de/how-to-get-picklist-labels-in-salesforce-flow/14-getobjectdefinition.png"
			alt="getObjectDefinition"&gt;&lt;figcaption&gt;
			&lt;p&gt;getObjectDefinition&lt;/p&gt;
		&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;With the DurableId from there, we can query the field definition:&lt;/p&gt;
&lt;figure&gt;&lt;img src="https://blog.uwe-braig.de/how-to-get-picklist-labels-in-salesforce-flow/15-getfielddefinition.png"
			alt="getFieldDefinition"&gt;&lt;figcaption&gt;
			&lt;p&gt;getFieldDefinition&lt;/p&gt;
		&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;And with the DurableId of the field definition we can finally query for the Picklist Value Info:&lt;/p&gt;
&lt;figure&gt;&lt;img src="https://blog.uwe-braig.de/how-to-get-picklist-labels-in-salesforce-flow/16-getpicklistvalueinfo.png"
			alt="getPicklistValueInfo"&gt;&lt;figcaption&gt;
			&lt;p&gt;getPicklistValueInfo&lt;/p&gt;
		&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Now we are able to reference the picklist label from the result of getPicklistValueInfo and use it in the screen display text or formula fields or text templates:&lt;/p&gt;
&lt;figure&gt;&lt;img src="https://blog.uwe-braig.de/how-to-get-picklist-labels-in-salesforce-flow/17-display-text-screen-component.png"
			alt="Display Text Screen Component"&gt;&lt;figcaption&gt;
			&lt;p&gt;Display Text Screen Component&lt;/p&gt;
		&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Our debug run will now look like this:&lt;/p&gt;
&lt;figure&gt;&lt;img src="https://blog.uwe-braig.de/how-to-get-picklist-labels-in-salesforce-flow/18-flow-output-solution2.png"
			alt="Flow Output"&gt;&lt;figcaption&gt;
			&lt;p&gt;Flow Output&lt;/p&gt;
		&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Considerations for this approach:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Independent of any screen components, i.e. suitable for screen flows, record-triggered flows and scheduled flows.&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://help.salesforce.com/s/articleView?id=sf.flow_considerations_limit_transaction.htm&amp;amp;type=5" target="_blank" rel="noopener"
 &gt;Per-Transaction Flow Limits&lt;/a&gt;: Each Get Record element counts as a SOQL query towards the &amp;ldquo;Total number of SOQL queries issued&amp;rdquo; limit. So be aware when it comes to getting labels for multiple picklist fields and/or with looping, for example, when working for record collections and the Data Table screen component.&lt;/li&gt;
&lt;li&gt;It will always deliver the label for the language of the running user, i.e. either the Master Label or the label as per translation workbench.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="solution-3-invocable-apex-methods"&gt;Solution #3: Invocable Apex Methods
&lt;/h2&gt;&lt;figure&gt;&lt;img src="https://blog.uwe-braig.de/how-to-get-picklist-labels-in-salesforce-flow/19-invocable-apex.png"
			alt="Invocable Apex Methods"&gt;
&lt;/figure&gt;

&lt;p&gt;For this approach, we need APEX code that can access the Schema objects and return labels for API values. Such APEX class can provide its methods as an &amp;ldquo;Invocable Method&amp;rdquo; which in turn can be accessed in a flow action.&lt;/p&gt;
&lt;p&gt;A good summary explanation for invocable actions can be found under &lt;a class="link" href="https://www.salesforceben.com/salesforce-invocable-apex-methods-a-complete-breakdown/" target="_blank" rel="noopener"
 &gt;https://www.salesforceben.com/salesforce-invocable-apex-methods-a-complete-breakdown/&lt;/a&gt; and an example implementation for such approach can be found under &lt;a class="link" href="https://unofficialsf.com/flow-action-get-picklist-values/" target="_blank" rel="noopener"
 &gt;https://unofficialsf.com/flow-action-get-picklist-values/&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Considerations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;All pros and cons of APEX coding, e.g. the need for developer capability and capacity and having proper test classes and code coverage in place.&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://help.salesforce.com/s/articleView?id=sf.flow_considerations_limit_transaction.htm&amp;amp;type=5" target="_blank" rel="noopener"
 &gt;Per-Transaction Flow Limits&lt;/a&gt;: Does not consume SOQL queries within the &amp;ldquo;Total number of SOQL queries issued&amp;rdquo; limit.&lt;/li&gt;
&lt;li&gt;Suitable for screen flows, scheduled flows and record-triggered after-save flows.&lt;/li&gt;
&lt;li&gt;Not suitable for record-triggered before-save flows as those do not allow sub flows and actions.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="conclusion"&gt;Conclusion
&lt;/h2&gt;&lt;p&gt;These approaches are basically just workarounds for a, from my view, major lack of feature in Salesforce flow. It definitely should be a standard feature to be able to chose whether we want to see the API value or the label in the flow. I suggest to vote the following ideas on the &lt;a class="link" href="https://ideas.salesforce.com/" target="_blank" rel="noopener"
 &gt;IdeaExchange&lt;/a&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://ideas.salesforce.com/s/idea/a0B8W00000PJ4sTUAT/show-picklist-label-instead-of-api-value-in-flow-datatable" target="_blank" rel="noopener"
 &gt;https://ideas.salesforce.com/s/idea/a0B8W00000PJ4sTUAT/show-picklist-label-instead-of-api-value-in-flow-datatable&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://ideas.salesforce.com/s/idea/a0B8W00000QN6h3UAD/show-value-or-trans-of-picklist-field-picked-for-data-table-column-screen-flow" target="_blank" rel="noopener"
 &gt;https://ideas.salesforce.com/s/idea/a0B8W00000QN6h3UAD/show-value-or-trans-of-picklist-field-picked-for-data-table-column-screen-flow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item></channel></rss>