skip to Main Content

let text = "’I’m the cook,’ he said, ‘it’s my job.’";

Console.log(text.replace(/(^|W)’|'(W|$)/g, ‘$1"$2’));

This code was meant to convert single quotes in sentences into double quotes without affecting single quotes used in contractions like aren’t

I understand the or statements and everything, even the pipe character. I just don’t understand how the capturing and replacement is done for each single quote. For every single quote that is replaced, what is the value of $1 and $2. Am confused?

I understand the regular expression used. I just don’t understand fully how it executes in this particular scenario.

At first it checks for if any beginning of line(^) proceeds a single quote. It finds one in the first word (‘I’m). So let’s say the first capture is the ^ indicating beginning of a line. So $1 is an empty space. Now I dnt get how it gets $2. Because I know it has to get $2 so it can place double quotes in between them to get $1"$2.

2

Answers


  1. I realize you said you understood the expression, but just for clarity /(^|W)'|'(W|$)/ is an alternation between two (outer) alternatives: (^|W)' and '(W|$). Each match will match one or the other of those subexpressions (which are also alternations, but that’s not important here). Since the alternatives have capture groups in them, that means that one of the groups will have something in it, and the other won’t, since only one alternative can match. When you give the replace method a replacement string with placeholders like $1 and $2, it’ll use a blank string for a capture group that has no value.

    To see exactly what the groups end up containing, you can pass a replacement function to replace instead of a replacement string. The function will receive the complete match as its first argument, the first capture group as its second argument, the second capture group as its third argument, etc.; the replacement is whatever the function returns. So if you swap the replacment string for a replacement function, you can use the debugger to look at the values of (in the below) c1 and c2 (or log them out as I have in the snippet below). Unlike when you give replace a replacement string, though, when you give it a function it’ll give you the value undefined (not a blank string) for a capture group that has no value, so we’ll need to handle that in our function.

    Here’s an example of doing that:

    let text = "'I'm the cook,' he said, 'it's my job.'";
    console.log(text.replace(/(^|W)'|'(W|$)/g, (match, c1, c2) => {
        // Build result, replacing `undefined` with `""`
        const result = `${c1 ?? ""}"${c2 ?? ""}`;
        // Show what was received and what result was produced
        console.log({match, c1, c2, result});
        // Return the result
        return result;
    }));
    .as-console-wrapper {
        max-height: 100% !important;
    }
    Login or Signup to reply.
  2. Both $1 and $2 will always exist for that expression, and will be treated as a zero-length string for uncaptured values. Let’s look at the results of a slightly modified version using a sentence that uses both capture groups.

    let text="'Eet's insensitif of you to call me a 'Grammar Nazi'!'";
    console.log(text.replace(/(^|W)'|'(W|$)/g, '[$$1=$1,$$2=$2]'));
    

    It replaces all the relevant matches with info contained in square brackets, which happens in four different places.

    [$1=,$2=]Eet's insensitif of you to call me a[$1= ,$2=]Grammar Nazi[$1=,$2=!][$1=,$2=]
    
    [$1=,$2=]  In match 1, line start was matched in $1 and $2 had no match.
    [$1= ,$2=] In match 2, a space was matched in $1 and $2 had no match.
    [$1=,$2=!] In match 3, $1 had no match and $2 matched the exclamation mark.
    [$1=,$2=]  In match 4, $1 had no match and $2 matched the line end.
    

    When both matches are taken together, you’ll always end up with one captured match and one empty match. So, the sequence $1"$2 will always fill in both $1 and $2 with a quotation mark between them but it’s guaranteed that one of those two captures will be empty.

    console.log(text.replace(/(^|W)'|'(W|$)/g, '$1"$2'));
    
    "Eet's insensitif of you to call me a "Grammar Nazi"!"
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search