Crashalot
Crashalot

Reputation: 34503

JavaScript negative lookahead regular expression producing wrong result

Assume the following URL: http://a2.mzstatic.com/us/ryoyo0/078/Purple100x100/v4/38/e3/b4/38e3b4a2-b422-8d1e-69f2-593fc035c9d4/mzl.vqhwzhhc.100x100-75.jpg

We want to replace the last occurrence of 100x100 with 256x256. The URL should read:

http://a2.mzstatic.com/us/ryoyo0/078/Purple100x100/v4/38/e3/b4/38e3b4a2-b422-8d1e-69f2-593fc035c9d4/mzl.vqhwzhhc.256x256-75.jpg

Here's our JavaScript replace method:

replace( /100x100(?!100x100)/, '256x256' )

Unfortunately, we consistently replace the first, not the last, occurrence.

What are we doing wrong?

Thanks!

Upvotes: 1

Views: 253

Answers (4)

Billy Moon
Billy Moon

Reputation: 58521

Yet another approach, due to possibly misunderstood intentions, which only replaces 100x100s that appear in the filename part (with no / appearing after it in the string) of the url.

replace(/100x100([^\/]*)$/, '256x256$1')

Works by...

  1. matching 100x100
  2. ... followed by capturing into group 1, zero or more, not forward slashes, with ([^\/]*)
  3. ... followed by the end of the string, $
  4. replacing with 256x256 and the first capture group, $1

If there is no match after the last / in the string, then no replacements are done.

N.B. I checked the speed of this answer, my other answer, and the answer of @Daedalus, and whilst I would normally expect avoiding assertions to speed up the regex, in this case, I have found them to be all identical in speed, and be very fast, running a couple of hundred thousand times a second on my computer.

Upvotes: 0

Billy Moon
Billy Moon

Reputation: 58521

Another way to do it...

replace( /(.*)100x100/, '$1256x256' )

Works by...

  1. Capturing everything up to the 100x100 as greedily as possible, with (.*), into group 1
  2. Matching the 100x100
  3. Replacing it with the captured group 1, by using $1 and then adding 256x256 in place of the non-captured match

N.B. This method can only work for the last match (or first if you make the initial capture un-greedy by adding a ? after the .*)

It is usually good to avoid using look-around assertions, for performance reasons, for compatibility with various regex implementations, and I believe also for readability.

EDIT: Test Script

var url = "http://a2.mzstatic.com/us/ryoyo0/078/Purple100x100/v4/38/e3/b4/38e3b4a2-b422-8d1e-69f2-593fc035c9d4/mzl.vqhwzhhc.100x100-75.jpg"

console.log(url.replace(/(.*)100x100/, '$1256x256'))

// OUTPUT:
// http://a2.mzstatic.com/us/ryoyo0/078/Purple100x100/v4/38/e3/b4/38e3b4a2-b422-8d1e-69f2-593fc035c9d4/mzl.vqhwzhhc.256x256-75.jpg

Upvotes: 3

Daedalus
Daedalus

Reputation: 1667

Try this: replace( /100x100(?!.*100x100)/, '256x256' )

Adding the .* accounts for additional characters between the first occurrence and the last occurrence of 100x100.

Note - While my answer describes what you did wrong in your pattern and how to fix it, the answer provided by Billy Moon is probably a better pattern for what you seem to be trying to do.

Upvotes: 4

James Tomasino
James Tomasino

Reputation: 3581

You could change your negative lookahead to look for the slash also. Since the second occurrence ends with a period, not a slash, that should solve it.

/100x100(?!\/)/

Upvotes: 0

Related Questions