4

I have the following function:

@testVisible private String selectBeforeKeyword (String remainingStepsAsString, String keyword)
{
    List<String> splitList = remainingStepsAsString.split('\\b'+keyword+'\\b');

    return (splitList.size() > 1)
                ? (splitList[0].trim())
                : null;
}

This works as I need it to. Yet I am not really looking to split the the string, but rather to capture the beginning of it. So, I'm thinking a more appropriate solution might be something like this:

@testVisible private String selectBeforeKeyword (String remainingStepsAsString, String keyword)
{
    Pattern keywordPattern = Pattern.compile('(.*)\\b'+keyword+'\\b');
    Matcher keywordMatcher = keywordPattern.matcher(remainingStepsAsString);

        return (keywordMatcher.matches())
                ? (keywordMatcher.group(0).trim())
                : null;
}

... however, this doesn't work, returning null instead.

What would the correct pattern to compile to make this work?

  • If you are getting the Pattern/Matcher solution to work, it would be interesting to see, how its performance compares to split(). Personally i would have used substringBefore(String). – Alexander Johannes Oct 7 '14 at 9:19
  • @AlexanderJohannes, I thought of using substringBefore, but I'm not sure that supports regex and I'm concerned with the whitespace before and after the keyword, which I don't want to assume will be spaces (e.g. it could be tabs or linefeeds). – Brian Kessler Oct 7 '14 at 9:44
  • Is there one backslash or two? If I'm not mistaken, each backslash needs to be escaped. If you simply use the backslashes for your regex pattern, you can use the indexOf to find their locations and capture the string between. Best RegEx pattern developer site I know of is gskinner.com. Has a nice testing utility to help you create & test them. Just don't rely on using the universal attributes since they don't apply to SF. – crmprogdev Oct 7 '14 at 11:57
  • @crmprogdev, Yes, it is also my understanding with SFDC you need to escape the backslashes, but I think I tried it both ways without success. I've seen a few sites which attempt to help with regex, but none is SFDC specific, so none deal with SFDC's quirks (like needing to escape the slashes). – Brian Kessler Oct 7 '14 at 14:37
  • Once you get the pattern you need, you can add any additional escapes. Gskinner's site does need escapes for backslashes (and others as documented in the reference). It has worked well for me in the past (note correct link provided). The main thing is that you can't use any of the flags, particularly the ignore case flag, with SF. The global flag is irrelevant as is the multiline in your situation. – crmprogdev Oct 7 '14 at 14:55
1

I was curious to see whether @Alexander was right, so I set up a simpler pattern matcher, and compared its runtime to the string split method. Each method was executed 1,000 times per run:

String keyword = 'World ';
String myString = 'Hello World.';

Integer stringSplitBegin; 
Integer stringSplitEnd;
Integer patternMatchBegin;
Integer patternMatchEnd;

stringSplitBegin = DateTime.now().millisecond();
for(Integer i=0; i < 1000; i++){
    String keyword = 'World';
    String myString = 'Hello World.';
    List<String> splitList = myString.split('\\b'+keyword+'\\b');

}
stringSplitEnd = DateTime.now().millisecond();


patternMatchBegin = DateTime.now().millisecond();
for(Integer i=0; i < 1000; i++){
    Pattern keywordPattern = Pattern.compile(keyword);
    Matcher keywordMatcher = keywordPattern.matcher(myString);
    
}
patternMatchEnd = DateTime.now().millisecond();

system.debug('stringSplit:  ' + (stringSplitEnd - stringSplitBegin) + 'ms');
system.debug('patternMatch: ' + (patternMatchEnd - patternMatchBegin) + 'ms');

I ran the code 5 times as anonymous Apex, and these were the results:

|DEBUG|stringSplit: 13ms :: patternMatch: 60ms

|DEBUG|stringSplit: 10ms :: patternMatch: 51ms

|DEBUG|stringSplit: 10ms :: patternMatch: 41ms

|DEBUG|stringSplit: 9ms :: patternMatch: 41ms

|DEBUG|stringSplit: 11ms :: patternMatch: 49ms

So as it works out, Alex was right. The string split method was consistently faster, by an average of 37.8ms.

I had been hoping to find a way to get the pattern matcher to work with a positive lookahead, but I don't think Apex supports them. No matter anyway, it seems!

Pattern keywordPattern = Pattern.compile('^.*(?=(' + keyword + '))');
| improve this answer | |
  • Thanks for finally solving the "riddle". Forgot I asked this. Cheers for the additional speed comparison, as well. :-) – Brian Kessler Oct 7 '18 at 16:00
  • No problem. @BrianKessler, do you know whether I'm right about Apex not supporting lookarounds? That's what I was trying to figure out when I found your question. – Shane Steinfeld Oct 7 '18 at 16:05
  • No idea... I really haven't worked with regex that much. – Brian Kessler Oct 8 '18 at 1:14

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.