Implementing a Sort for an Array

Posted on

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 and let, I am not too excited about that

  • If you remove the numeric test, then you no longer need isNumeric as well

  • compareCaseSenitiveStrings should be compareCaseSensitiveStrings

  • 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.

Leave a Reply

Your email address will not be published. Required fields are marked *