I’ve been thinking about writing a post like this for a while but have put it off because, frankly, it’s pretty embarrassing…
Today, I saw a post in the Free Code Camp Forums that made it clear, however, that I’m not the only one that goes through this. So hopefully this post will encourage other beginners to keep going, even if their code looks terrible in their own eyes…
The purpose of this post won’t be to walk through the algorithms and explain everything that’s going on, but rather to emphasize how different two answers to the same problem can be.
Codewars
Codewars, as I’ve mentioned in previous posts, is a great way to practice solving algorithm challenges. It can be pretty humbling at times, though, because after you solve the problem, you have the opportunity to look at other people’s solutions.
So, when you solve what you thought was a difficult challenge, only to see that others have solved it in one or two lines of code, it makes you wonder if you’re writing in the same language. When you realize you are, you wonder further if this coding thing is really for you…
The upside to this, and, in my opinion, the real strength of Codewars, is that you can learn from these solutions… I’ve only done a handful of algorithms on this site so far, but I’m going to show my first and last solutions as examples of how things can improve.
My First Codewars Solution
The first algorithm I solved at Codewars was called ‘Decode the Morse code’. Try not to laugh….
The site’s description of the algorithm is as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
/* In this kata you have to write a simple Morse code decoder. While the Morse code is now mostly superceded by voice and digital data communication channels, it still has its use in some applications around the world. The Morse code encodes every character as a sequence of "dots" and "dashes". For example, the letter A is coded as ·−, letter Q is coded as −−·−, and digit 1 is coded as ·−−−. The Morse code is case-insensitive, traditionally capital letters are used. When the message is written in Morse code, a single space is used to separate the character codes and 3 spaces are used to separate words. For example, the message HEY JUDE in Morse code is ···· · −·−− ·−−− ··− −·· ·. NOTE: Extra spaces before or after the code have no meaning and should be ignored. In addition to letters, digits and some punctuation, there are some special service codes, the most notorious of those is the international distress signal SOS (that was first issued by Titanic), that is coded as ···−−−···. These special codes are treated as single special characters, and usually are transmitted as separate words. Your task is to implement a function decodeMorse(morseCode), that would take the morse code as input and return a decoded human-readable string. For example: decodeMorse('.... . -.-- .--- ..- -.. .') //should return "HEY JUDE" The Morse code table is preloaded for you as MORSE_CODE dictionary, feel free to use it. In Java, the table can be accessed like this: MorseCode.get('.--'). In C#, the preloaded Dictionary can be accessed like this: MorseCode.Get('.--');. All the test strings would contain valid Morse code, so you may skip checking for errors and exceptions. In C#, tests will fail if the solution code throws an exception. Please keep that in mind. (This is mostly because else the engine would simply ignore the tests, resulting in a "valid" solution.) Good luck! */ |
So, in thinking about the solution to this algorithm, I immediately pictured various ‘for’ loops to split the Morse code into words, then letters. Then I could replace the Morse letters for real letters and reassemble the message using more ‘for’ loops.
Here is what I came up with…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
decodeMorse = function(morseCode){ //accept morse code string as argument var str = arguments[0]; //trim leading and trailing whitespace str = str.trim(); //create 2d array by word then letter var arr = str.split(' '); for(var j = 0; j < arr.length; j++){ arr[j] = arr[j].split(' '); } //loop through array exchanging morse code for letters for(var k = 0; k < arr.length; k++){ for(var i = 0; i < arr[k].length; i++){ arr[k][i] = MORSE_CODE[arr[k][i]]; } } //compress array into words and return for (var l = 0; l < arr.length; l++){ arr[l] = arr[l].join(''); } return arr.join(' '); } |
I was feeling pretty good about myself (it worked after all!) until I saw some of the top rated solutions…
Here are a couple:
1 2 3 4 5 6 7 8 9 10 11 |
//by czyzykowski decodeMorse = function(morseCode){ function decodeMorseLetter(letter) { return MORSE_CODE[letter]; } function decodeMorseWord(word) { return word.split(' ').map(decodeMorseLetter).join(''); } return morseCode.trim().split(' ').map(decodeMorseWord).join(' '); } |
1 2 3 4 5 |
//by Azuaron decodeMorse = function(mc) { return mc.trim().split(' ').map(function(v) {return v.split(' ').map(function(w) {return MORSE_CODE[w];}).join('');}).join(' '); }; |
1 2 3 4 5 |
//by prettycoder decodeMorse = function(morseCode){ return morseCode.trim().split(' ').map(a => MORSE_CODE[a] || ' ').join('').replace(/\s+/g, ' '); } |
Pretty big difference, huh? These 3 solutions, put together end to end, took fewer lines than my 1 solution did…
I want to make a couple points about this…
My Latest Codewars Solution
First, if you use these solutions to learn from, you can get more clever with your own solutions, and in the end, make yourself a better programmer.
Here’s an example of an algorithm I submitted a couple days ago.
Algorithm details:
1 2 3 4 5 |
/* Write an algorithm that takes an array and moves all of the zeros to the end, preserving the order of the other elements. moveZeros([false,1,0,1,2,0,1,3,"a"]) // returns[false,1,1,2,1,3,"a",0,0] */ |
My solution:
1 2 3 4 5 6 7 8 9 10 11 12 |
var moveZeros = function (arr) { var length = arr.length; var newArr = arr.filter(function(num){return num!==0}); while(newArr.length < length){ newArr.push(0); } return newArr; } |
Most of the top solutions were variations of this:
1 2 3 |
var moveZeros = function (arr) { return arr.filter(function(x) {return x !== 0}).concat(arr.filter(function(x) {return x === 0;})); } |
Ok, so was this last attempt better?? Sure… Still room for improvement? You betcha…
Looking through past examples definitely helped me put together a more concise solution for this problem, but I still have a lot to learn…
For instance, looking at the answers to this algorithm reminded me that the array.filter() method doesn’t change the array on which it is called. So, once I filtered the non-zero values into a new array, I still had the original array to work with. I could have used it again to add the zeros to the end of the new array.
Hopefully, next time I come across a problem like this I’ll remember the more elegant solution.
In my experience, learning a new skill, especially one as difficult as programming, is all about repetition and practice. So I encourage you (and myself too) to use sites like Codewars to practice these skills over and over.
I also encourage you to take the time to look through other people’s solutions and learn from them. It’ll probably help with your approach to your next one.
It ain’t necessarily about having the shortest solution…
The second point I want to make is that the shortest solution isn’t necessarily the best solution.
We can argue that ‘for’ loops are actually faster than native functions like array.filter() and array.map(), and they are, but that argument is mostly semantic. Unless you are writing an application that demands extremely high performance, it is unlikely that the user will see a difference between the two.
I believe, therefore, that prioritization should be placed on the readability of the code you write.
Going through the process of teaching myself how to code, I’ve read many examples of code written by others, and can say for certain that all code is not created equal.
Some code is nicely written and readable… Other code is like a plate of spaghetti and impossible to decipher.
Let’s go back to the answers to the first algorithm. My code, while drawn out and overly complicated, is fairly easy to follow. The last two solutions listed, while being the shortest, are not that easy to read (at least to a newbie like me).
I’d argue that there’s gotta be a happy medium, maybe something like the one written by czyzykowski. Their solution is logical, modular and very easy to follow.
Feel free to comment below if you disagree, like I said, I’m still new to this, so maybe those last 2 examples are perfectly readable to someone who’s been at this a while.
Bottom Line
I think the takeaway from all this should be – don’t worry if your code looks like a fanewgi wrote it, just keep plugging away…
Read code others have written and keep writing more of your own. Before you know it, you’ll look back at code you wrote a while ago and realize just how far you’ve come!!
That’s it for now, I hope this helped straighten out the code…
-Jeremy