views:

131

answers:

3

In a given string, I'm trying to verify that there are at least two words, where a word is defined as any non-numeric characters so for example

// Should pass
Phil D'Sousa
Billy - the - Kid

// Should Fail
Joe
454545 354434

I thought this should work:

(\b\D*?\b){2,}

But it does not.

+6  A: 

You forgot to allow for a space between your "words":

\b\D*?\b(?:\s+\b\D*?\b)+
           ^^^

There are a number of other problems I can see:

I'm also rather suspicious of your definition of "word". Any non-numeric character also includes punctuation and whitespace. That's probably not what you really mean. You might want to try defining word like this instead: [^\d\s]+. This still allows words to contain punctuation, but it disallows both numerals and whitespace.

There is a problem with your usage of word boundaries - if a word can consist of punctuation then words beginning or ending on punctuation won't have a word boundary so your regular expression will miss them.

Are you searching for a string that contains at least two "words", and possibly also some numbers? Or must the string consist only of "words" and no numbers at all anywhere in the string? Currently your regular expression is looking for two consecutive "words" but in general they might not be consecutive.

Mark Byers
Some names contain punctuation: hyphens, apostrophies, etc. But you regEx only allows 2 words, not 2 or more words, doesn't it? But I didn't realize \D includes spaces. How do I say "Non-Numeric, Non-Space"?
Will
@Will: I've updated my answer to address both your points.
Mark Byers
+2  A: 

You can globally search for a "word" and check the length of the .match() if a match is found:.

If two or more words are found, you're good:

var matches = string.match(/\b[^\d\s]+\b/g);
if ( matches && matches.length >= 2 ) 
    { /* Two or more words ... */ }; 

You can define a word as \b[^d\s]+\b, which is a word boundary \b, one or more non digits and non whitespaces [^d\s]+, and another word boundary \b. You have to make sure to use the global option g for the regex to find all the possible matches.

You can tweak the definition of a word in your regex. The trick is to make use of the length property of the .match(), but you should not check this property if there are no matches, since it'll break the script, so you must do if (matches && matches.length ...).

Additionally it's quite simple to modify the above code for X words where X is either a number or a variable.

jsFiddle example with your 4 examples

Peter Ajtai
A: 

This seems to work, for your definition of "word".

/((\W|^)\D+?(\W|$).*){2}/

Here are your four examples, plus some more added after editing and fixing this answer:

>>> r = /((\W|^)\D+?(\W|$).*){2}/
/((\W|^)\D+?(\W|$).*){2}/
>>> !!"Phil D'Sousa".match(r)
true
>>> !!"Billy - the - Kid".match(r)
true
>>> !!"Joe".match(r)
false
>>> !!"54545 354434".match(r)
false
>>> !!"foo bar baz".match(r)
true
>>> !!"123 foo 456".match(r)
false
>>> !!"123 foo 456 bar".match(r)
bcherry
You're right that the `*` should be a `+`. This is also a mistake in my answer, so I should fix that too. However that's not the only problem with the OPs regular epxression. Try your suggestion on this: `54545 a 354434`. My reading of the OPs question is that this should fail.
Mark Byers
good catch. Editing with `/((\W|^)\D+?(\W|$).*){2}/`.
bcherry