3

I have a string and I want to replace every 'i' that is NOT following/followed by any other i and replace it with 'z`. I know that there is negative lookahead and lookbehind.

Results shoud be:

i => z
iki => zkz
iiki => iikz
ii => ii
iii => iii

I tried to use this:

/(?<!i)i(?!i)/gi

and it failed and thrown an error: Invalid regex group.

Yet

/i(?!i)/gi

works fine, but matches second "i" in this: "ii".

Is there some other way?

What is support for lookbehind in JS if there is any?

SkillGG
  • 647
  • 4
  • 18

3 Answers3

8

In your case you don't really need look-behind:

'iiki'.replace(/i+/g, (m0) => m0.length > 1 ? m0 : 'z')

You can just use a function as the replacement part and test the length of the matched string.

Here are all your test cases:

function test(input, expect) {
  const result = input.replace(/i+/g, (m0) => m0.length > 1 ? m0 : 'z');
  console.log(input + " => " + result + " // " + (result === expect ? "Good" : "ERROR"));
}

test('i', 'z');
test('iki', 'zkz');
test('iiki', 'iikz');
test('ii', 'ii');
test('iii', 'iii');
melpomene
  • 84,125
  • 8
  • 85
  • 148
5

Lookbehind in JavaScript regular expressions is quite new. As of this writing, it's only supported in V8 (in Chrome, Chromium, Brave...), not by other engines.

There are many questions with answers here about how to work around not having lookbehind, such as this one.

This article by Steven Levithan also shows ways to work around the absense of the feature.

I want to replace every 'i' that is NOT following/followed by any other i and replace it with 'z`

That's fairly easy to do without either lookahead or lookbehind, using placeholders and a capture group. You can capture what follows the i:

const rex = /i(i+|.|$)/g;

...and then conditionally replace it if what was captured isn't an i or series of is:

const result = input.replace(rex, (m, c) => {
    return c[0] === "i" ? m : "z" + c;
});

Live Example:

const rex = /i(i+|.|$)/g;
function test(input, expect) {
    const result = input.replace(rex, (m, c) => {
        return c[0] === "i" ? m : "z" + c;
    });
    console.log(input, result, result === expect ? "Good" : "ERROR");
}

test("i", "z");
test("iki", "zkz");
test("iiki", "iikz");
test("ii", "ii");
test("iii", "iii");
vsemozhebuty
  • 12,992
  • 1
  • 26
  • 26
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
1

One hack you can use in this case. is changing the offset value based on match.

let arr = ['i','iki','iiki','ii','iii', 'ki']

arr.forEach(e=>{
  let value = e.replace(/i(?!i)/g, function(match,offset,string){
    return offset > 0 && string[offset-1] === 'i' ? 'i' : 'z'
  })
  console.log(value)
})
Code Maniac
  • 37,143
  • 5
  • 39
  • 60