19

How can I find the last occurrence of a word with word boundaries? I created a regex expression of /\btotal\b/ for the word. How would I use search() to find the last occurrence of this expression? Thanks in advance for the help!

maioman
  • 18,154
  • 4
  • 36
  • 42
Sujay Garlanka
  • 319
  • 2
  • 5
  • 16

4 Answers4

30

You can use negative lookahead to get the last match:

/(\btotal\b)(?!.*\b\1\b)/

RegEx Demo 1

RegEx Demo 2

(?!.*\1) is negative lookahead to assert that captured group #1 i.e. total word is NOT present ahead of the present match.

anubhava
  • 761,203
  • 64
  • 569
  • 643
  • I tried the demo, but the match information section on website with your demo says: "No match groups were extracted. This means that your pattern matches but there were no (capturing (groups)) in it that matched anything in the subject string." I was wondering what this meant? I ask because I tried your method, but I get -1 when I use it with the search function on a string that shows matches on regex101.com. – Sujay Garlanka Jul 20 '16 at 22:15
  • 1
    I also get two matches sometimes? How does that happen if this finds the last total? – Sujay Garlanka Jul 20 '16 at 22:24
  • [Check this demo](https://regex101.com/r/lI0fF7/5) Also keep in mind it can potentially find `total` in **two different lines** not on same line. – anubhava Jul 21 '16 at 14:07
  • Is it possible to find the last total of the two matches? By the way, thanks for the continued help! – Sujay Garlanka Jul 22 '16 at 15:23
  • Yes sure you can use [`/(\btotal\b)(?![^]*\b\1\b)/`](https://regex101.com/r/lI0fF7/7) – anubhava Jul 22 '16 at 15:34
  • Sorry, one more question anubhava. Could regex also include numbers and underscore as a word boundary so 'total' could be found in the following example? https://regex101.com/r/lI0fF7/8 – Sujay Garlanka Jul 24 '16 at 04:15
  • @anubhava Is there any way to extract the last currency value? I have the expression ([+-]?\$[0-9]{1,3}(?:,?[0-9]{3})*\.[0-9]{2}) to match all currency values, but only need the last one. If necessary, I will do programmatically but wanted to see if there was a regex I could use. [Regex Sample](https://regex101.com/r/4ZyN2e/1) Should I open a new question? – William Humphries Jun 18 '20 at 13:58
  • 1
    @WilliamHumphries: I suggest using `.*(([+-]?\$[0-9]{1,3}(?:,?[0-9]{3})*\.[0-9]{2}))` and use first capture group. You can check demo here: https://regex101.com/r/4ZyN2e/2 – anubhava Jun 18 '20 at 14:08
  • @anubhava I'm facing issue with capturing the first group, I added what I thought was a non-capturing group (?:.*) before the expression but still returning a whole sentence. Also tried \1 suggested from [Regex Buddy](https://www.regular-expressions.info/backref.html) but couldn't get a full match of the value $60,000. [Regex101 Sample](https://regex101.com/r/4ZyN2e/3) – William Humphries Jun 18 '20 at 14:47
  • 1
    Just use `.*([+-]?\$[0-9]{1,3}(?:,?[0-9]{3})*\.[0-9]{2})` and use 1st capture group – anubhava Jun 18 '20 at 14:58
14

Without using the lookaheads but using the same regex (having applied the g, i.e. global, flag), the option would be to match the string with regex and get the last match.

var matches = yourString.match(/\btotal\b/g);
var lastMatch = matches[matches.length-1];
nicael
  • 18,550
  • 13
  • 57
  • 90
0

I like @anubhava's answer. And in case your string might contain line breaks, you can try the following Negative Lookahead:

(\btotal\b)(?!.*[\r\n]*.*\1)

See an example on regex101. Also see below, we replace the last occurrence of total with <TOTAL>

var str = `
something total abc 
total 


foo total anything here
total



Anything total done!
`;

var finalStr = str.replace(/(\btotal\b)(?!.*[\r\n]*.*\1)/, "<TOTAL>");

console.log(finalStr)

And you will get:

something total abc 
total 


foo total anything here
total



Anything <TOTAL> done!
Yuci
  • 27,235
  • 10
  • 114
  • 113
0

@anubhava answer is correct for single line inputs, but the idea is correct thank you for sharing it!

I think his answer can be improved (but please do correct me if I'm wrong) with version below:

/\b(total)\b(?![\s\S]*\b\1\b)/

The difference here is that [\s\S]* will effectively match any character, new lines included. Also word boundary \b are not really needed in the capturing group.

In the linked 2nd example by @anubhava it's matching only the very first total word found because the pattern (?!.*\b\1\b) will not consider newlines. You can verify by adding the word total somewhere at the beginning of the sample mail text.

Demo link: https://regex101.com/r/VlhRWM/3

I'm just a bit worried about performance of the [\S\s]* pattern.

Also I think that the reported errors in comments, for not found matching group, are related to the regex ECMAScript flavor selected in the linked demo, using PCRE is reporting correctly the match.


NB.
This pattern will consider a valid match in the ENTIRE input provided. If you need a per line match, you need to enable the /g global flag and use @anubhava answer!

Gruber
  • 2,196
  • 5
  • 28
  • 50