0

I have a field called ATC__c on Contact which is a multipicklist with values 'A,B,C'. I'm trying to check if the newly inserted Contact has one of the values in a multipicklist field. If the newly inserted Contact has one of those values, it should trigger another class. So far i have this piece of code.

 String selectValues= '';
 Set<String> values = new Set<String>();

        for(Contact ct: Trigger.New){

               values.add(ct.ATC__c);
               selectValues += String.valueOf(values);

            }
<next piece of code is already written>......

 Set<String> originalvalues = new Set<String>{'A','B,'C'};
for (Contact cont : Trigger.new) {
 if(originalvalues.contains(selectValues) //Is this the correct way to check if the new contact has one of the values in the multipicklist values??
 {
.........next code...........
}

}
2

Multi-picklist values are stored as semi-colon separated values in a string. It is better to extract the values into a set and check for set membership than to use string contains since you can easily get false positives if you have options in your picklist where one value is contained within another (such as "south" and "south east").

The code fragment you have is broken by the "selectValues" string. What it is doing with "values" seems reasonable, though not quite right, for collecting the full list of selected picklist items across all the contacts being processed in the trigger.

The following modifications are required:

Set<String> values = new Set<String>();

for (Contact contact: Trigger.new) {
    // Split the multi-picklist values in ATC__c into the separate selected values,
    // looking out for an empty selection
    List<String> selections = contact.ATC__c != null ? contact.ATC__c.split(';') : null;

    if (selections != null && !selections.isEmpty()) {
        // These selections are added to the over-all set of selections across all contacts
        values.addAll(selections);
    }
}

How you then use this data (the values set) is up to you, using simple set membership methods like containsAll as covered in the documentation.

1
  • Cool. thanks for your response.
    – Kalyan
    May 28 '20 at 18:11
2

Looks like you have the right general idea, but not quite the correct execution.

Multipicklists store the selected values as a semicolon-delimited list. To be able to compare that against your target value set, originalValues, you need to turn that semicolon-delimited string into a List or a Set. Thankfully, this is easy to do. List<String> separatedValues = myString.split(';');

There's also an issue with your approach. Based on the code you have so far, you're gathering the CT__c values of all contact records being inserted, and making a decision on whether or not to call your extra code based on that (as opposed to making the decision based on what individual records contain).

If you're not using the first loop anywhere else in your code, I'd suggest getting rid of it.

A better approach would look like this

// be aware that set comparisons are case-sensitive
Set<String> targetValues = new Set<String>{'A', 'B', 'C'};
// Best to declare a list to store the records that should be processed further.
// Salesforce best practices usually boil down to "work on collections, and don't
//   query/dml in loops"
List<Contact> contactsToProcess = new List<Contact>();
for(Contact cont :trigger.new){
    // What we'll be doing next would mutate the original set, so make a clone
    //   to keep the original targetValues untouched
    Set<String> clonedTarget = targetValues.clone();

    // Gotta make sure CT__c isn't null before continuing (lest we get a null
    //   pointer exception)
    if(String.isNotBlank(cont.CT__c)){
        // retainAll keeps all the values that appear in both collections
        clonedTarget.retainAll(cont.CT__c.split(';'));

        // Here, we're saying "process this record if it matches _exactly 1_ of
        //   the target values".
        // You'll have to modify this if that's not an accurate description of what you want.
        // Checking if the resulting set isn't empty should suffice if you want to say
        //   "process this record if it matches _any_ of the target values"
        if(clonedTarget.size() == 1){
            contactsToProcess.add(cont);
        }
    }
}
1
  • Wow. great explanation, thank you. helped a lot.
    – Kalyan
    May 28 '20 at 18:11
1

The best way to do something like this is to leverage Set and the removeAll method. removeAll takes a Set as an argument and calculates the difference between two sets. If any of the values of the argument set are contained in the set that the method is called on, removeAll returns true.

See documentation on removeAll.

Set<String> selected;
Set<String> valuesOfInterest = new Set<String>{'A', 'B', 'C'};

for(Contact ct: Trigger.New){
    if(String.isBlank(ct.ATC__c)) {
        // skip if nothing is selected
        continue;
    }

    selected = new Set<String>(ct.ATC__c.split(';'));

    if(selected.removeAll(valuesOfInterest)) {
        // ... do something
    }
}

3
  • 1
    this actually worked. Thanks.
    – Kalyan
    May 28 '20 at 18:10
  • 1
    Don't forget your null check.
    – Adrian Larson
    May 28 '20 at 19:03
  • Thanks @Kalyan, if my answer worked for you please consider marking it accepted.
    – nbrown
    Jun 3 '20 at 13:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.