37

I'm trying to match the start and end character of a string to be the same vowel. My regex is working in most scenarios, but failing in others:

var re = /([aeiou]).*\1/;
re.test(str);

Sample input:

  • abcde, output - false (Valid)
  • abcda, output - true (Valid)
  • aabcdaa, output - true (Valid)
  • aeqwae, output - true (Not valid)
  • ouqweru, output - true (Not valid)
dodov
  • 5,206
  • 3
  • 34
  • 65
Nesh
  • 2,389
  • 7
  • 33
  • 54

4 Answers4

70

You need to add anchors to your string.

When you have, for example:

aeqwae

You say the output is true, but it's not valid because a is not the same as e. Well, regex simply matches the previous character (before e), which is a. Thus, the match is valid. So, you get this:

[aeqwa]e

The string enclosed in the brackets is the actual match and why it returns true.

If you change your regex to this:

/^([aeiou]).*\1$/

By adding ^, you tell it that the start of the match must be the start of the string and by adding $ you tell it that the end of the match must be the end of the string. This way, if there's a match, the whole string must be matched, meaning that aeqwae will no longer get matched.

A great tool for testing regex is Regex101. Give it a try!

Note: Depending on your input, you might need to set the global (g) or multi-line (m) flag. The global flag prevents regex from returning after the first match. The multi-line flag makes ^ and $ match the start and end of the line (not the string). I used both of them when testing with your input.

dodov
  • 5,206
  • 3
  • 34
  • 65
  • The only thing missing from the above answer for me, was, how do you then re-use the value matched at the start for the end? ... what is the \1 for? – Beezer Feb 12 '19 at 21:33
  • "Capturing group. (regex) Parentheses group the regex between them. They capture the text matched by the regex inside them into a numbered group that can be reused with a numbered backreference." So I guess I just answered my own question. Thank you for the awesomely cool answer. Now, onward and forwards to understand (g) (m) et. al. – Beezer Feb 13 '19 at 13:30
  • To understand what **\1** actually does, you might want to see this : https://javascript.info/regexp-backreferences#backreference-by-number-n – Maulik Pipaliya Joyy Jan 23 '20 at 15:16
  • You might have to add another \ to escape, to avoid a potential "Octal literals are not allowed in strict mode" error. – Devin B. Nov 08 '20 at 01:26
6

Just a different version of @Hristiyan Dodov answer that I have written for fun.

regex = /^(a|e|i|o|u).*\1$/

const strings = ['abcde', 'abcda', 'aabcdaa', 'aeqwae', 'ouqweru']

strings.forEach((e)=>{
  const result = regex.test(e)
  console.log(e, result)
})
Pablo
  • 2,137
  • 1
  • 17
  • 16
6

Correct answer is already mentioned above, just for some more clarification:

regEx= /^([a,e,i,o,u])(.*)\1$/

Here, \1 is the backreference to match the same text again, you can reuse the same backreference more than once. Most regex flavors support up to 99 capturing groups and double-digit backreferences. So \99 is a valid backreference if your regex has 99 capturing groups.visit_for_detail

Enlico
  • 23,259
  • 6
  • 48
  • 102
beingGeek
  • 61
  • 1
  • 2
1
/^([aeiou])[a-z]\1$/

just a bit of improvement, to catch alphabet letters.

geekme
  • 294
  • 2
  • 4
  • 1
    You regex expression `[a-z]` must match and only match once alphabet letters, for example, "aa" and "axxa" will not matched. But you give a good advise, I use `/^([aeiou])[a-z]*\1$/` to instead. – Jun Yu Jan 13 '21 at 07:22
  • To catch only one you can add `{1}` like so /^([aeiou]{1})[a-z]+\1$/ – Jacksonkr Sep 01 '21 at 16:44