getRankBetween method

String getRankBetween({
  1. required String firstRank,
  2. required String secondRank,
})

Generate a lexo rank between two rank.

the firstRank should be lower than the secondRank unless the reorderPosition is true

Implementation

// inspired by https://medium.com/whisperarts/lexorank-what-are-they-and-how-to-use-them-for-efficient-list-sorting-a48fc4e7849f
String getRankBetween(
    {required String firstRank, required String secondRank}) {
  final firstPositionIsLower = firstRank.compareTo(secondRank) < 0;
  if (!firstPositionIsLower) {
    if (reorderPosition) {
      final f = firstRank;
      firstRank = secondRank;
      secondRank = f;
    } else {
      throw LexoRankException(
        'First position must be lower than second. '
        'Got firstRank $firstRank and second rank $secondRank',
      );
    }
  }

  /// Make positions equal
  while (firstRank.length != secondRank.length) {
    if (firstRank.length > secondRank.length) {
      secondRank += "a";
    } else {
      firstRank += "a";
    }
  }
  var firstPositionCodes = [];
  firstPositionCodes.addAll(firstRank.codeUnits);
  var secondPositionCodes = [];
  secondPositionCodes.addAll(secondRank.codeUnits);
  num difference = 0;
  for (int index = firstPositionCodes.length - 1; index >= 0; index--) {
    /// Codes of the elements of positions
    var firstCode = firstPositionCodes[index];
    var secondCode = secondPositionCodes[index];

    /// i.e. ' a < b '
    if (secondCode < firstCode) {
      /// ALPHABET_SIZE = 26 for now
      secondCode += alphabetSize;
      secondPositionCodes[index - 1] -= 1;
    }

    /// formula: x = a * size^0 + b * size^1 + c * size^2
    final powRes = pow(alphabetSize, firstRank.length - index - 1);
    difference += (secondCode - firstCode) * powRes;
  }
  var newElement = "";
  if (difference <= 1) {
    /// add middle char from alphabet
    newElement = firstRank +
        String.fromCharCode('a'.codeUnits.first + alphabetSize ~/ 2);
  } else {
    difference ~/= 2;
    var offset = 0;
    for (int index = 0; index < firstRank.length; index++) {
      /// formula: x = difference / (size^place - 1) % size;
      /// i.e. difference = 110, size = 10, we want place 2 (middle),
      /// then x = 100 / 10^(2 - 1) % 10 = 100 / 10 % 10 = 11 % 10 = 1
      final diffInSymbols =
          difference ~/ pow(alphabetSize, index) % (alphabetSize);
      var newElementCode =
          firstRank.codeUnitAt(secondRank.length - index - 1) +
              diffInSymbols +
              offset;
      offset = 0;

      /// if newElement is greater then 'z'
      if (newElementCode > 'z'.codeUnits.first) {
        offset++;
        newElementCode -= alphabetSize;
      }

      newElement += String.fromCharCode(newElementCode);
    }
    newElement = newElement.split('').reversed.join();
  }
  return newElement;
}