Machine
Machine

Reputation: 80

How to ignore order in regex?

I'm using a regex, to validate some stuff in my angular2 application:

Validators.pattern('\d*F{1}N{1}K?J?')

I just want to allow digits (0-9) and the letters F N K J. K and J should be optional, F and N have to be in there exactly one time.

Does anyone know, how to ignore the order of letters and digits? Not it just matches, if I exactly insert chars in this order: 012FNKJ. KJFN012 doesn't match at all. :(

Upvotes: 1

Views: 5606

Answers (4)

Mistalis
Mistalis

Reputation: 18279

Here is a suggestion. It matches "any string of K, J and digits that contains exactly one F and exactly one N".

^[KJ0-9]*F[KJ0-9]*N[KJ0-9]*$

Explanation:

  • [KJ0-9]* between zero and unlimited times K, J and numbers
  • F[KJ0-9]* a F followed by K, J and numbers
  • N[KJ0-9]* a N followed by K, J and numbers

Note that if N can be before F in your pattern, you may want to use

^[KJ0-9]*F[KJ0-9]*N[KJ0-9]*|[KJ0-9]*N[KJ0-9]*F[KJ0-9]*$

As you can not make a regex count basically, that is the cleaner way I see to check it with a regex.

Test it on regex101

Upvotes: 0

SamWhan
SamWhan

Reputation: 8332

You can use a negative look-ahead combined with a back reference:

^(?=.*F)(?=.*N)(?:\d|([FNKJ])(?!.*\1))*$

It starts by using look-aheads to make sure the F and the N are in there.Then it matches digits, or a letter from the allowed group capturing the letter followed by a negative look-ahead to ensure it doesn't repeat itself. The previous alternative is then repeated, until the end of the string.

See it here at regex101.

Upvotes: 2

Tezra
Tezra

Reputation: 8833

You need to use | to say "this pattern or this pattern", and explicitly lay out both patterns.

[0-9KJ]*F[0-9KJ]*N[0-9KJ]*|[0-9KJ]*N[0-9KJ]*F[0-9KJ]*

This is why Regex is not good for fuzzy matching. You'd be better validating with [0-9FNKJ]* and string.containsOne('F', 'N') (psudo code)

Upvotes: 0

Tom Lord
Tom Lord

Reputation: 28285

Here is a pure regex answer to your problem (whitespace has been added for improved readability):

^
(?=[^F]*F[^F]*$)
(?=[^N]*N[^N]*$)
(?=[^K]*K?[^K]*$)
(?=[^J]*J?[^J]*$)
[FNKJ\d]+$

Explanation:

  • Each of the four (?=...) sections of the pattern are lookaheads. They are saying, for example, "the string must contain any number of non-Ks, then maybe one K, then any number more non-Ks".
  • The final part of the regex is saying "the whole string must contain only F, N, K, J and digits".

However, if possible then I would recommend implementing a non-regex solution to this problem. My answer above is difficult to understand, and way slower than a simple function could be (O(n)!).

Some pseudo-code that solves this without a regex, in O(n):

function check_valid(string) {
  found_f = false
  found_n = false
  found_k = false
  found_j = false

  for(letter in string) {
    switch(letter) {
      case 'F':
        if(found_f) { return false }
        found_f = true
        break;
      case 'N':
        if(found_n) { return false }
        found_n = true
        break;
      case 'K':
        if(found_k) { return false }
        found_k = true
        break;
      case 'J':
        if(found_j) { return false }
        found_j = true
        break;
      case 0: case 1: case 2: case 3: case 4:
      case 5: case 6: case 7: case 8: case 9:
        break;
      default:
        return false; 
    }
  }

  return(found_f & found_n)
}

Upvotes: 3

Related Questions