Problem
Yesterday, I gave an interview after a long time, and I was asked a question which I couldn’t solve on the spot.
I had to implement a sort function for a Array of strings in JavaScript, with the following requirements:
- The Array will always contain Strings
- For sorting, Upper Case has higher priority than Lowe Case Letters, with Numbers having the Least.
- Assume that Array.sort() cannot be used
- The performance of the sorting is not a factor; i.e. Bubble sort is fine.
I was thinking of the solution today, and came up with this:
function sortArray(items) {
let itemcount = items.length;
for (var i = 0; i < itemcount; i++) { //number of passes
for (var j = 0; j < (itemcount - 1 - i); j++) { //for checking all pairs in a given pass
let a = items[j];
let b = items[j + 1];
if (compareCaseSenitiveStrings(a, b) < 0) {
console.log("Switch", a, b)
items[j + 1] = a;
items[j] = b;
}
}
}
//return
return items;
}
//Function to compare two given strings, and return the index of the higher priority one
function compareCaseSenitiveStrings(a, b) {
//Inner functions
function compareCharacters(a, b) {
//check the case first()
if (a.getFirstCase() == b.getFirstCase()) {
let charDiff = a.codePointAt(0) - b.codePointAt(0);
return -Math.sign(charDiff)
} else {
//different cases
//We need to pass the id of the one which is higher;
let requiredCaseOrder = ["NONE", "LOWER", "UPPER"];
return requiredCaseOrder.indexOf(a.getFirstCase()) - requiredCaseOrder.indexOf(b.getFirstCase());
}
}
function isNumeric(n) {
return !isNaN(parseFloat(n)) && isFinite(n);
}
//End of inner functions
//===============================================
//Start of function here
//First check if both are numbers
if (isNumeric(a) && isNumeric(b)) {
//return the index of smaller number (since it has higher priority)
return -Math.sign((parseFloat(a) - parseFloat(b)));
}
//Now check that lengths of both is greater than 0
if ((a.length > 0) && (b.length > 0)) {
//check the first elements of this string
let result = compareCharacters(a[0], b[0]);
if (result != 0) {
//this means one input has higher priority, lets return that
return result;
} else {
//both are equal
//get the result of remaining characters
return compareCaseSenitiveStrings(a.substring(1), b.substring(1));
}
} else {
//length of atleast one of these strings is zero.
//return the index of the longer element
let lengthDiff = a.length - b.length;
return Math.sign(lengthDiff);
}
}
String.prototype.getFirstCase = function() {
let caseEnum = ["LOWER", "NONE", "UPPER"];
var caseIndex = 1;
if (this == this.toLowerCase()) {
caseIndex--;
}
if (this == this.toUpperCase()) {
caseIndex++;
}
return caseEnum[caseIndex];
};
Is this code readable enough?
Are there any other Design Patterns that could be applied? Any other points which could be improved?
Solution
Sorting is hard.
-
console.log( sortArray([ '123', '9' , '1' , '11' ]) );
returns["1", "9", "11", "123"]
, that does not sound good. After removing the numeric test I get["11", "123", "1", "9"]
which is better but not intuitive, I would have expected the 1 up front. This shows that you cannot just write the code, you need to build a set of tests to validate your code. Especially during interviews. -
String.prototype.getFirstCase
is very cute, but it needs commenting for lesser readers. -
You are mixing
var
andlet
, I am not too excited about that -
If you remove the numeric test, then you no longer need
isNumeric
as well -
compareCaseSenitiveStrings
should becompareCaseSensitiveStrings
-
I would convert 2 line comments to 1 line comment for readability, and an overall review of what comments really add value
-
Other than the code was very readable
function sortArray(items) {
let itemcount = items.length;
switches = 1 // dummy variable to enter the while loop
while (switches) { // while there were switches made in the previous iteration
switches = 0;
// this while loop will stop when there are no switches, i.e. the array has already been sorted
for (var j = 0; j < (itemcount - 1); j++) { //for checking all pairs in a given pass
let a = items[j];
let b = items[j + 1];
if (compareCaseSenitiveStrings(a, b) < 0) {
console.log("Switch", a, b); // semi-colon added
items[j + 1] = a;
items[j] = b;
switches += 1;
}
}
}
//return
return items;
}
(In the future, use !== to compare to 0).
Personally, I would change the sorting algorithm as above, because I think this method would require fewer iterations, although I haven’t timed it.