<p>I'm trying to write a regex function that will identify and replace a single instance of a match within a string without affecting the other instances.  For example, I have this string:</p>

<pre><code>12||34||56
</code></pre>

<p>I want to replace the second set of pipes with ampersands to get this string:</p>

<pre><code>12||34&amp;&amp;56
</code></pre>

<p>The regex function needs to be able to handle x amount of pipes and allow me to replace the nth set of pipes, so I could use the same function to make these replacements:</p>

<pre><code>23||45||45||56||67 -&gt; 23&amp;&amp;45||45||56||67

23||34||98||87 -&gt; 23||34||98&amp;&amp;87
</code></pre>

<p>I know that I could just split/replace/concat the string at the pipes, and I also know that I can match on <code>/\|\|/</code> and iterate through the resulting array, but I'm interested to know if it's possible to write a single expression that can do this.  Note that this would be for Javascript, so it's possible to generate a regex at runtime using <code>eval()</code>, but it's not possible to use any Perl-specific regex instructions.</p>

[{'answer_id': 36191, 'author': 'Sam Hasler', 'author_id': 2541, 'author_profile': 'https://Stackoverflow.com/users/2541', 'pm_score': 5, 'selected': True, 'text': '<p>here\'s something that works:</p>\n\n<pre><code>"23||45||45||56||67".replace(/^((?:[0-9]+\\|\\|){n})([0-9]+)\\|\\|/,"$1$2&amp;&amp;")\n</code></pre>\n\n<p>where n is the one less than the nth pipe, (of course you don\'t need that first subexpression if n = 0)</p>\n\n<p>And if you\'d like a function to do this:</p>\n\n<pre><code>function pipe_replace(str,n) {\n   var RE = new RegExp("^((?:[0-9]+\\\\|\\\\|){" + (n-1) + "})([0-9]+)\\|\\|");\n   return str.replace(RE,"$1$2&amp;&amp;");\n}\n</code></pre>\n'}, {'answer_id': 1181513, 'author': 'Community', 'author_id': -1, 'author_profile': 'https://Stackoverflow.com/users/-1', 'pm_score': 1, 'selected': False, 'text': '<pre><code>function pipe_replace(str,n) {\n    m = 0;\n    return str.replace(/\\|\\|/g, function (x) {\n        //was n++ should have been m++\n        m++;\n        if (n==m) {\n            return "&amp;&amp;";\n        } else {\n            return x;\n        }\n    });\n}\n</code></pre>\n'}, {'answer_id': 7958627, 'author': 'Nathan Long', 'author_id': 4376, 'author_profile': 'https://Stackoverflow.com/users/4376', 'pm_score': 5, 'selected': False, 'text': '<h1>A more general-purpose function</h1>\n\n<p>I came across this question and, although the title is very general, the accepted answer handles only the question\'s specific use case.</p>\n\n<p>I needed a more general-purpose solution, so I wrote one and thought I\'d share it here.</p>\n\n<h2>Usage</h2>\n\n<p>This function requires that you pass it the following arguments:</p>\n\n<ul>\n<li><code>original</code>: the string you\'re searching in</li>\n<li><code>pattern</code>: either a string to search for, or a RegExp <strong>with a capture group</strong>. Without a capture group, it will throw an error. This is because the function calls <code>split</code> on the original string, and <a href="https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/String/split#Example:_Capturing_parentheses" rel="nofollow noreferrer">only if the supplied RegExp contains a capture group will the resulting array contain the matches</a>.</li>\n<li><code>n</code>: the ordinal occurrence to find; eg, if you want the 2nd match, pass in <code>2</code></li>\n<li><code>replace</code>: Either a string to replace the match with, or a function which will take in the match and return a replacement string.</li>\n</ul>\n\n<h2>Examples</h2>\n\n<pre><code>// Pipe examples like the OP\'s\nreplaceNthMatch("12||34||56", /(\\|\\|)/, 2, \'&amp;&amp;\') // "12||34&amp;&amp;56"\nreplaceNthMatch("23||45||45||56||67", /(\\|\\|)/, 1, \'&amp;&amp;\') // "23&amp;&amp;45||45||56||67"\n\n// Replace groups of digits\nreplaceNthMatch("foo-1-bar-23-stuff-45", /(\\d+)/, 3, \'NEW\') // "foo-1-bar-23-stuff-NEW"\n\n// Search value can be a string\nreplaceNthMatch("foo-stuff-foo-stuff-foo", "foo", 2, \'bar\') // "foo-stuff-bar-stuff-foo"\n\n// No change if there is no match for the search\nreplaceNthMatch("hello-world", "goodbye", 2, "adios") // "hello-world"\n\n// No change if there is no Nth match for the search\nreplaceNthMatch("foo-1-bar-23-stuff-45", /(\\d+)/, 6, \'NEW\') // "foo-1-bar-23-stuff-45"\n\n// Passing in a function to make the replacement\nreplaceNthMatch("foo-1-bar-23-stuff-45", /(\\d+)/, 2, function(val){\n  //increment the given value\n  return parseInt(val, 10) + 1;\n}); // "foo-1-bar-24-stuff-45"\n</code></pre>\n\n<h2>The Code</h2>\n\n<pre><code>  var replaceNthMatch = function (original, pattern, n, replace) {\n    var parts, tempParts;\n\n    if (pattern.constructor === RegExp) {\n\n      // If there\'s no match, bail\n      if (original.search(pattern) === -1) {\n        return original;\n      }\n\n      // Every other item should be a matched capture group;\n      // between will be non-matching portions of the substring\n      parts = original.split(pattern);\n\n      // If there was a capture group, index 1 will be\n      // an item that matches the RegExp\n      if (parts[1].search(pattern) !== 0) {\n        throw {name: "ArgumentError", message: "RegExp must have a capture group"};\n      }\n    } else if (pattern.constructor === String) {\n      parts = original.split(pattern);\n      // Need every other item to be the matched string\n      tempParts = [];\n\n      for (var i=0; i &lt; parts.length; i++) {\n        tempParts.push(parts[i]);\n\n        // Insert between, but don\'t tack one onto the end\n        if (i &lt; parts.length - 1) {\n          tempParts.push(pattern);\n        }\n      }\n      parts = tempParts;\n    }  else {\n      throw {name: "ArgumentError", message: "Must provide either a RegExp or String"};\n    }\n\n    // Parens are unnecessary, but explicit. :)\n    indexOfNthMatch = (n * 2) - 1;\n\n  if (parts[indexOfNthMatch] === undefined) {\n    // There IS no Nth match\n    return original;\n  }\n\n  if (typeof(replace) === "function") {\n    // Call it. After this, we don\'t need it anymore.\n    replace = replace(parts[indexOfNthMatch]);\n  }\n\n  // Update our parts array with the new value\n  parts[indexOfNthMatch] = replace;\n\n  // Put it back together and return\n  return parts.join(\'\');\n\n  }\n</code></pre>\n\n<h2>An Alternate Way To Define It</h2>\n\n<p>The least appealing part of this function is that it takes 4 arguments. It could be simplified to need only 3 arguments by adding it as a method to the String prototype, like this:</p>\n\n<pre><code>String.prototype.replaceNthMatch = function(pattern, n, replace) {\n  // Same code as above, replacing "original" with "this"\n};\n</code></pre>\n\n<p>If you do that, you can call the method on any string, like this:</p>\n\n<pre><code>"foo-bar-foo".replaceNthMatch("foo", 2, "baz"); // "foo-bar-baz"\n</code></pre>\n\n<h2>Passing Tests</h2>\n\n<p>The following are the Jasmine tests that this function passes.</p>\n\n<pre><code>describe("replaceNthMatch", function() {\n\n  describe("when there is no match", function() {\n\n    it("should return the unmodified original string", function() {\n      var str = replaceNthMatch("hello-there", /(\\d+)/, 3, \'NEW\');\n      expect(str).toEqual("hello-there");\n    });\n\n  });\n\n  describe("when there is no Nth match", function() {\n\n    it("should return the unmodified original string", function() {\n      var str = replaceNthMatch("blah45stuff68hey", /(\\d+)/, 3, \'NEW\');\n      expect(str).toEqual("blah45stuff68hey");\n    });\n\n  });\n\n  describe("when the search argument is a RegExp", function() {\n\n    describe("when it has a capture group", function () {\n\n      it("should replace correctly when the match is in the middle", function(){\n        var str = replaceNthMatch("this_937_thing_38_has_21_numbers", /(\\d+)/, 2, \'NEW\');\n        expect(str).toEqual("this_937_thing_NEW_has_21_numbers");\n      });\n\n      it("should replace correctly when the match is at the beginning", function(){\n        var str = replaceNthMatch("123_this_937_thing_38_has_21_numbers", /(\\d+)/, 2, \'NEW\');\n        expect(str).toEqual("123_this_NEW_thing_38_has_21_numbers");\n      });\n\n    });\n\n    describe("when it has no capture group", function() {\n\n      it("should throw an error", function(){\n        expect(function(){\n          replaceNthMatch("one_1_two_2", /\\d+/, 2, \'NEW\');\n        }).toThrow(\'RegExp must have a capture group\');\n      });\n\n    });\n\n\n  });\n\n  describe("when the search argument is a string", function() {\n\n    it("should should match and replace correctly", function(){\n      var str = replaceNthMatch("blah45stuff68hey", \'stuff\', 1, \'NEW\');\n      expect(str).toEqual("blah45NEW68hey");\n    });\n\n  });\n\n  describe("when the replacement argument is a function", function() {\n\n    it("should call it on the Nth match and replace with the return value", function(){\n\n      // Look for the second number surrounded by brackets\n      var str = replaceNthMatch("foo[1][2]", /(\\[\\d+\\])/, 2, function(val) {\n\n        // Get the number without the [ and ]\n        var number = val.slice(1,-1);\n\n        // Add 1\n        number = parseInt(number,10) + 1;\n\n        // Re-format and return\n        return \'[\' + number + \']\';\n      });\n      expect(str).toEqual("foo[1][3]");\n\n    });\n\n  });\n\n});\n</code></pre>\n\n<h2>May not work in IE7</h2>\n\n<p>This code may fail in IE7 because that browser incorrectly splits strings using a regex, as discussed <a href="https://stackoverflow.com/questions/4417931/javascript-split-regex-bug-in-ie7">here</a>. [shakes fist at IE7]. I believe that <a href="http://blog.stevenlevithan.com/archives/cross-browser-split" rel="nofollow noreferrer">this</a> is the solution; if you need to support IE7, good luck. :)</p>\n'}, {'answer_id': 74004810, 'author': 'Prashant', 'author_id': 1855068, 'author_profile': 'https://Stackoverflow.com/users/1855068', 'pm_score': 0, 'selected': False, 'text': '<p>Thanks Binda, I have modified the code for generic uses:</p>\n<pre><code>private replaceNthMatch(original, pattern, n, replace) {\n    let m = -1;\n    return original.replaceAll(pattern, x =&gt; {\n        m++;\n        if ( n == m ) {\n            return replace;\n        } else {\n            return x;\n        }\n    });\n}\n</code></pre>\n'}]
['https://Stackoverflow.com/questions/36183', 'https://Stackoverflow.com', 'https://Stackoverflow.com/users/2289/']