0

I am struggling with the below example. So basically I want to see if a contact is the Chief Operations Officer. Many of these individuals have the title "COO". So I am running into the following issue where when I try to pick up on "coo" - All contacts in our database with the title Coordinator are being tagged as Chief Operating Officers.

My question is how do I efficiently bypass this issue in Apex with the String Class.

https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_methods_system_string.htm

I know I can write if statements to catch this specific issue, but the code comes out disorganized and is not a scalable as I find more of these similar types of issues.

Example:

String1 = 'Coordinator'
String2 = 'COO'

if(String1.containsIgnoreCase('coo')) -> True (This should output to False)

if(String2.containsIgnoreCase('Coordinator') -> True

3
  • Your first example, using containsIgnoreCase(), is behaving correctly. That is, it is ignoring the case of the letters in the comparison. Since 'coordinator' starts with 'coo', it returns true. Perhaps you mean to use contains() instead. – Derek F Jan 29 '20 at 4:19
  • @DerekF You should have added that as an answer. – sfdcfox Jan 29 '20 at 4:36
  • @sfdcfox I wrote that just before I went to bed, and wasn't certain that String.contains() was, in fact, case-sensitive. I suppose I'll write a proper answer now, though. – Derek F Jan 29 '20 at 14:14
4

I think that case sensitivity is a bit of a red herring here. What distinguishes a COO from occurrences of the same letters is the context around it.

To pick those up, you need something like a regular expression. Here's a regex which does the job:

List<String> strings = new List<String> {
        'Coordinator',
        'Uncoordinated',
        'Uncoo',
        'COO',
        'The COO',
        'COO King',
        'The coo King'
};

Pattern cooPattern = Pattern.compile('(?i)(\\W|^)coo(\\W|\\z)');

for(String s : strings) {
    System.debug(s + ': ' + cooPattern.matcher(s).find());
}

Breaking down the regex:

  • (?i) makes it case-insensitive. It's quite possible that someone would write COO in lowercase
  • (\\W|^) says the thing before the COO ought to be the start of the line, or a non-word character (e.g. a space, or punctuation)
  • coo is COO, case-insensitive due to the marker at the start
  • (\\W|\\z) says that the thing after COO should be the end of the input, or a non-word character

See documentation for the Java Pattern class and Apex Pattern class for details.

1

The examples you gave are incorrect.

Since you were using containsIgnoreCase(), the case is (unsurprisingly) ignored. Since "Coordinator" does, in fact, contain "coo", the containsIgnoreCase() method is rightly returning true.

String.contains(), on the other hand, is case-sensitive

You can run the following snippet to verify

String s1 = 'COO';
String s2 = 'Coordinator';

system.debug(s1.containsIgnoreCase(s2)); // displays false, since the entirety of "Coordinator"
                                         //   is not contained in the string "COO"
system.debug(s2.containsIgnoreCase(s1)); // displays true, since "COO" is found at the start
                                         //   of "Coordinator" (when case is ignored)

system.debug(s1.contains(s2));           // false
system.debug(s2.contains(s1));           // This one is false this time because contains()
                                         //   is case-sensitive ("COO" != "Coo")

I agree with Aidan in that case-sensitivity isn't necessarily the best way to approach this problem.

Given that we're working with job titles here, we probably don't need to dive into RegEx. While you will see some oddball titles here and there, I believe job titles are fairly standardized. A simple string equality check should be sufficient here.

system.debug(s1 == 'COO'); // Returns true, because normal string equality is case-insensitive

If you need to expand this (e.g. I want to separate COO, CFO, CEO, Comptroller, Janitor, and put everyone else into the same bucket), then I'd suggest using a map to avoid a chain of if-else (or a switch).

// Declare a map, and initialize it to contain the target buckets
Map<String, List<Contact>> employeeBucketsMap = new Map<String, List<Contact>>{
    'coo' => new List<Contact>(),
    'cfo' => new List<Contact>(),
    'janitor' => new List<Contact>(),
    'others' => new List<Contact>()
};

for(Contact c :contacts){
    // The contains() method of a set, and containsKey() for a map _is_ case-sensitive
    //   so we need to do some pre-processing
    String title = c.Title.toLowerCase();
    String bucket;

    // (condition) ? (expression if true) : (expression if false) is a ternary operator
    // Equivalent to an if-else, but perhaps a little shorter to write
    employeeBucketsMap.containsKey(title) ? bucket = title : bucket = 'others';

    employeeBucketsMap.get(bucket).add(c);
}
2
  • Can contains be used to recognize spaces -> 'coo ' vs 'coo' – Matthew Metros Jan 29 '20 at 14:57
  • 1
    @MatthewMetros I encourage you to modify my example to test that (run as anonymous apex through the developer console). Like both Aidan and I said, though, depending on case-sensitivity isn't a great idea here. – Derek F Jan 29 '20 at 15:02

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.