Latest news about Bitcoin and all cryptocurrencies. Your daily crypto news habit.
All developers want to write readable and maintainable code. It has always been my personal programming goal too. However, one time I did something different and totally opposite. It was a fun journey and I regret nothing. Please, sit comfortably and I will tell you the story about how overcomplicated JavaScript code impressed theĀ judges.
TSH Coding Challenge
At The Software House, we have a recurring event called Coding Challengeāāāyou write a piece of code in the language you choose and solve a problem specified by the judges. What are the criteria for winning? You literally write anything that fits into requirements, but also convince the jury that your code is simply the best. ( Tina Turner playing in the background ). So, if you want to get more votes, do something clever, surprising or elegant in your work (or better, everything atĀ once!).
This edition, the goal was to write a simple function that validates postal codes. The function should take a string to test and return a boolean answer if the provided code is OK: a simple TRUE or FALSE. Every postal code should have five digits with an optional separator between second and third character. Space, dash and underline are the only allowed separators here. In other words, those postal codes areĀ correct:
- 00000
- 12345
- 67ā890
- 00 111
- 66_666
And thoseĀ arenāt:
- a1ā234
- 12.345
- 7890
- 111ā11
Unimpressive expressions
The first solution that came up to everyoneās mind were regular expressions. This is the best practice when it comes to format validation, especially when we have established rules about whatās good and whatāsĀ not.
Of course, I didnāt use a regular expression.
Not because it would be the easiest method. I simply expected that most people at the Coding Challenge will use them (I was totally right by the way). So, I took a different approach. I wanted to go against the tide and make my code as complicated as possible. However, making a mess in the code seemed too trivial so I decided to be more specific andā¦ play some golf. CodeĀ golf.
Do developers playĀ golf?
Rules of golf are pretty simple: players try to put a small white ball in multiple holes of a grass field, using as few golf club strokes as possible. Code golf is kinda similar. You have to write code with the lowest number of characters. You can say that instead of counting the ball strokes we count the keyboard taps. The shorter the code is, the better is your result, but it still has to work according toĀ rules.
Going with a regular expression is still a good solution here because the code will be super short. Thatās true, but also far too easy (and itās a Coding Challenge, for Peteās sake!). I decided that a regular expression is a no-no, as it would kill the fun. I also hoped that this would impress theĀ judges.
Oh, and I picked JavaScript, because whyĀ not?
Enough theory, now letās write some messyĀ code!
Solid foundations
I started working on my code by writing some tests in the Jest framework. I made 18 small unit tests for a single function to cover all edge cases. A perfect Test-Driven Development approach (actually not so muchāāāIām jokingĀ around).
All the tests failed as expected. And I havenāt even started writing mine postal code validator yet. To do it, I had to create the functionĀ first.
I used an anonymous arrow function because its signature is shorter compared to a normal one with the function and return keywords. Donāt mind the long variable names. I am keeping them only to make the code more readable for now and I will remove themĀ later.
The digitsĀ problem
Now, we need to check if the code has only digits in the appropriate places. Notice that postal code is really just a number with an optional separator between some digits. In other words, if we ignore that separator, we can just treat the postal code as a five digit number! So, letās use that in our validation. I did it by picking the first two characters, last three characters and checking if they make a number together.
For example, for the code 12ā345 we are slicing the ā12ā from the start, ā345ā from the end and concatenating it into ā12345ā. Note that this code is insensitive to how many separator characters you have in between or how long the provided code isāāāwe always use the first two and last three characters. Now, we have to validate the result. I cast it to a number type with the Number function. It returns a proper number value from the string when possible and NaN (not a number type) otherwise.
Side note: we can already make this code a little bit shorter by using a + sign operator instead of the Number function. This code will work exactly the same as the oneĀ above:
Weāre not done yet because our function is currently returning the number, but it has to return a TRUE or FALSE value. We can easily fix it by using a double exclamation sign which works as a double negation: it changes everything into a booleanĀ value.
Great! But thereās a problem here which I need to address. If the code is, for example, ā1a-234ā after converting it to a number we will get a NaN, which is FALSE after casting to boolean. Good! 12ā345 will be properly interpreted as a number (12345), and we will get TRUE and the end. Fantastic! How about 00ā000? The ā00000ā will be nicely interpreted as a number (zero) but casting it to bool will give as FALSE because zero isĀ false!
Thatās why we have to do a trick here with adding 1 to the result. It will take only two more characters in our code, but it will make our number always positive and true. No more problem with the 00ā000 edgeĀ case.
Unfortunately, our code still has some problems. Did you know that in JavaScript some numbers can contain letters? Itās for writing binary or hexadecimal numbers. Because of those numbers, 0b10111 and 0x30 are totally valid. Those 0b and 0x prefixes are key here. In the checking function, we must take care of them and ābrokeā inputs like 0b-101 and 0x-111 which would return TRUE during the validation. Luckily for us, itās very easy. We may use the fix done earlier and tweak it a little bit by moving the added 1 at the beginning of concatenation.
Now, postal code like 0b-101 will be concatenated into 10b101 which is not a binary number anymore because of the wrong prefix. Neat, we are slowly moving forward! Thereās only one more edge case here to solve, for numbers with a dot: ā0.123ā. They are not a valid postal code, but currently, our validator will return TRUE for them because they are a proper number. So, what can we do? The same thing as last time: somehow break those numbers with a full stop. And I decided to do it byā¦ introducing a second fullĀ stop!
Now, whenever a postal code has only digits, the validator will be internally making one full stop-something number, e.g.: 12ā345 into 1.12345 which is a proper number. This will return TRUE. However, when the function will be provided with a problematic postal code like ā1.-234ā then the validator will be making a 1.1.234 which is nowhere proper and will return FALSE. The only downside of this solution is that we lose three more characters in our code golf. This is something I can live with forĀ now.
Phew, the hardest part is over. Now letās talk about separators.
Meet the separators
In the examples above, we were using mostly dashes but there are three separators allowed: dash, underline, space and an empty string (no separator). If weād like to define an array with them, weād getĀ this:
A lot of apostrophes. We are playing code golf here so no way weāre leaving it like that. Do you know the spread operator from ES6? It allows putting elements from one array into one another without manually rewriting all elements. LikeĀ this:
Ok, but why am I talking about it? Because strings can also be treated as arrays soāāāthanks to it, and the spread operatorāāāwe can save some characters by spreading the string of allowed āvisibleā separators.
This array is the same as the one above. Here, however, we use only 12 characters instead of 15. Do you remember three lost characters from fixing the dot problem before? Weāve just found them and made up for them in the codeĀ golf.
Of course, we not only need to check if the separator is of an allowed type but, also, if itās in the proper place. Now, weāll repeat the trick with splitting the string: weāll take all the characters from the provided code starting after the second character and ending before the third character from the end. If the separator is correct, it should be included in the array of available separators.
Thanks to it, if someone provides, for example, ā12___345ā we will be testing ā___ā. Also, a code without the separator, like 12345, will be tested with an empty string which is in our array of allowed separators.
By gluing everything together, I gotĀ this:
The finalĀ stroke
Currently, almost everything is done and done. The only way the user can bypass our validator is by passing a string of three or four digits. Letās analyze this exampleā: ā1234ā. The function will concatenate the fix ā1.ā, the first two characters ā12ā, and the last three ā234ā. We will get ā1.12234ā which is a fine number and our validator will not find the problem. So, we have to additionally check if the string has at least 5 characters. How to do it? By just checking if the fifth character of the string exists. I added the checking at the beginning of the functionĀ body.
That was the last thing we had to check. Other incorrect postal codes, such as a code with too many characters, will be rejected by mechanisms Iāve added earlier. Knowing that we can jump straight to removing all unnecessary spaces and characters from the code, gives us more points in the codeĀ golf.
The final result? Only 86 characters. And the validator passes each of the 18 unit tests. You can check it out yourself by using the repl.itĀ service.
And the trophy goesĀ toā¦
Did judges at The Software House like my crazy solution? Yes. I got most of the votes and I won the Coding Challenge. My award? A bottle of great Belgian beer! You may ask yourself, is this the best solution I could come up with? I am not sure. Maybe someone can rewrite this validator using even fewer characters?
Exercising your brain and changing your patterns once in a while will do wonders! If you, dear reader, want to beat me in the code golf, please send your code to this email address. I will be excited to see a better solution. We want to have the same chances, so just remember the rules: use plain JavaScript and no regular expressions are allowed. GoodĀ luck!
PS. Exponentially growingĀ problems
Almost two weeks passed from publishing this articleāāāit was shared in many placed and sparked some comments on Reddit, Facebook, Twitter and our social e-mail address. The most recurring topic was exponential numbers. Yes, I admit, I forgot aboutĀ them.
To put it simply, when someone would try to validate ā1e234ā, then the validator would return TRUE which is a false negative. Itās because in JavaScript, except the aforementioned hexadecimal and binary numbers, we have also a special notation for writing very big or very smallĀ numbers.
The so-called exponential or scientific notation is made out of two normal numbers separated by the letter āeā. To get the āfull valueā you have to take the first number (significand) and multiply it by the 10 raised to the power of the second number (exponent). So ā12e3ā becomes 12 * 10Ā³ = 12 * 1000 = 12000. This comes in handy when you want to put in a code the distance between planets. If however, you want to describe how small the human body cells are, you can put negation in front of the second number. For example ā34e-5ā is 34 * 10^-5 = 34 * 0.00001 =Ā 0.00034.
So now we know whatās the problem here, and we can finally solve it. Letās start with adding a new unitĀ test.
How we will make it pass? We donāt need to do a revolution in our validator code, we only need to use the same trick weāve used for the full stop problem. We will add an extra āeā character to the number made internally in our validator. Now when someone will use it the postal code, the validator will return FALSE because no number can have two āeā characters. Someone proposed to put it between sliced parts of the postal code but I will add it to the existing prefix to save some characters.
I had to add an extra zero before the character so we wouldnāt get an invalid ā1.eā in the number. This solution will make our tests pass but we must be aware of some consequences. At this moment, the validator will always internally create a number written in exponential notation. It means we can have problems with the limitation of numbers range or with the negative exponent. For that, I added four moreĀ tests.
The first test checks what will happen if the user tries to generate a really big number. The correct ā99999ā postal code will generate internally a string ā1.0e99999ā and then the validator will try to make a number of it. However, it wonāt enable to calculate the exact value because it will be too big for the JavaScript engine. The operation will return Infinity value, and luckily for us, the Infinity is TRUE after casting to bool. Our validator will work correctly and the first test will beĀ green.
The second test is similar, however, it checks what will happen if someone tries to sneak a minus to force the validator to calculate some very, very small number. By accident, this test will also work because internally made ā1.0e-9999ā will generate a number so small the browser will change it to zero. And zero is FALSE when cast toĀ bool.
Unfortunately, the third extra test will fail. It also has a minus in the first place but the number will not be too small. From an invalid postal code ā-0001ā the validator will concatenate a string ā1.0e-0001ā which equals 0.1 and it is a valid number. Thatās a problem because our validator would accept this value and return TRUE. How to fix it? Note that minus in the exponent is only valid in the first place. If we somehow disallow the user to put the minus in the first place in exponent then we will make the test green. The easiest way to do it is, of course, adding an extra zero after the āeā character.
It makes the code look even more like black magic! But heyāāāthe test is green now. This trick will also fix the fourth test. Numbers like ā1e+23ā are also valid and we donāt want any pluses in our postal code. So now all tests pass again and we can call itĀ day.
Currently the full solution takes 89 characters but hopefully, it covers all the edge cases. Test it yourselfĀ here!
Thanks to everyone who mentioned this problem on the discussion thread or contacted me with direct messages. I also want to say that many people send me their great ideas on how to improve the code and make it even shorter! Because the contest is still on, I wonāt spoil other peopleās ideas. But man, I could have saved so many charactersā¦ Who knows, maybe even someday I will publish and review those solutions. š
The article was written by Marcin Gajda and was first published on The Software House Blog. Visit the blog for more articles on best development practices and software outsourcing tips.
How I ruined my JavaScript code and still won the Coding Challenge was originally published in Hacker Noon on Medium, where people are continuing the conversation by highlighting and responding to this story.
Disclaimer
The views and opinions expressed in this article are solely those of the authors and do not reflect the views of Bitcoin Insider. Every investment and trading move involves risk - this is especially true for cryptocurrencies given their volatility. We strongly advise our readers to conduct their own research when making a decision.