2

I'm doing some code cleanup and I'm looking at my regexes. I have an extremely simple one:

(ARA|CHI|FRE|GER|ITA|JPN|RUS|SPA)\s[0-9]{3}-[0-9]{2}

It basically validates course identifiers for a specific department (Modern Languages and Literatures) at my university, e.g. FRE 101-01 is valid, CIS 101-01 is not; I cannot simply use [A-Z]{3}.

I already have a class constant DEPARTMENTS:

DEPARTMENTS = {
    arabic: "ARA",
    chinese: "CHI",
    french: "FRE",
    german: "GER",
    italian: "ITA",
    japanese: "JPN",
    russian: "RUS",
    spanish: "SPA"
}

This is Ruby, but could probably apply to any language: is it a good idea to map the enum into the regex instead of explicitly re-listing the values? Here is what I mean. In Ruby, I could build the regex like:

(DEPARTMENTS.values.join('|'))\s[0-9]{3}-[0-9]{2}

It would probably look similar in other languages, maybe less concise in static ones. I have several enums that go into this kind of regex validation. The advantage to the latter approach, that I see, is that I only need to update one spot in the code if we add or remove a department. The sacrifice is a little bit of readability. Granted, the regex and enum live in the same class (in all cases) so it would be fairly hard to forget to update the regex too, and finding DEPARTMENTS to know what values it has would take all of 5 extra seconds...

Is:

(DEPARTMENTS.values.join('|'))\s[0-9]{3}-[0-9]{2}

more appropriate than:

(ARA|CHI|FRE|GER|ITA|JPN|RUS|SPA)\s[0-9]{3}-[0-9]{2}

?

2 Answers 2

1

Personally, I wouldn't even use regex to validate the string :) It's simple enough validation to be written by hand and, by doing so, your problem goes away.

But, assuming you want to keep the regex then I favour your first option:

(DEPARTMENTS.values.join('|'))\s[0-9]{3}-[0-9]{2}

It is DRY and its only drawback is that it makes the regex slightly harder to reason about. But, considering being "hard to reason about" is kind of regex's thing, I wouldn't work about it too much.

If, however, you find yourself in the quirky position where you choose to keep your original approach:

(ARA|CHI|FRE|GER|ITA|JPN|RUS|SPA)\s[0-9]{3}-[0-9]{2}

Then you can always write a unit test that verifies the above regex includes all (and only all) of your departments.

5
  • What do you mean by "written by hand"? Commented Aug 14, 2016 at 19:49
  • I mean writing out the code the parses the string and validates the correct format. Something like: Check length, Check position of delimiters (` ` and - in this case), Split string by delimiters and validate tokens.
    – MetaFight
    Commented Aug 14, 2016 at 19:52
  • Well that seems like a lot more work than just writing a simple regex... I have a lot of these types of validations, but they're not complex at all. They just all happen to have an enum as part of the validation. Commented Aug 14, 2016 at 20:27
  • It may be more upfront work, but it's more maintainable. The intention of code is typically much easier to read than the intention of a regex. Consider what will happen over time when your validation logic becomes more complicated.
    – MetaFight
    Commented Aug 14, 2016 at 20:52
  • True in most cases, I would agree. However my validations are highly unlikely to change - university department courses, student enrollment, all really basic stuff :) I will keep your advice in mind for the future though! Commented Aug 14, 2016 at 21:02
0

It is a good idea to only maintain the list of departments in one place.

While your technique is valid I would prefer to just extract the department using a regex group.

([A-Z]{3})\s[0-9]{3}-[0-9]{2}

Now the department should be available in group 1

When extracted, just check that the department is included in your list.

6
  • The problem is that the validation no longer works. [A-Z]{3} matches CIS and all sorts of other junk, which is not what I want. Only the enum list of departments is valid :) The whole point of this is to make the validation simpler. I can write a single regex with my proposed solution, but with yours I have to write a function to test the regex and test inclusion in the department list. Commented Aug 14, 2016 at 16:03
  • Yes you have to write a two line function. Why is that a problem? Commented Aug 14, 2016 at 16:11
  • Because I'm cleaning up code, and mapping the enum values when creating the regex would make it so that I don't have to write the function; when validating, it actually totals 3 lines of code ;) but code that I don't have to write and have cluttering my class :) Commented Aug 14, 2016 at 16:13
  • Unless we are codegolfing we should choose the most readable solution to a given problem. Commented Aug 14, 2016 at 16:17
  • Like I said in my question, I already have a solution that doesn't require an extra function. And mapping the values from the enum isn't really that less readable, given that the enum and regex are right next to each other in the class. I don't think your solution cleans anything up (which is what I'm trying to do; DRY out my model). Commented Aug 14, 2016 at 16:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.