Skip to content

Instantly share code, notes, and snippets.

@RichardHightower
Last active January 12, 2023 22:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save RichardHightower/1d64d0c958a7643c8b0b573c08138e1f to your computer and use it in GitHub Desktop.
Save RichardHightower/1d64d0c958a7643c8b0b573c08138e1f to your computer and use it in GitHub Desktop.
Video 5 Show Notes

Video 5 show notes

  • Link to this page: https://bit.ly/vid5B62P2Rick

Tags

#java #functionalprogramming #scala #base62 

Title: Porting Base62Encoder/Decoder from Java to functional Scala

Porting Base62Encoder/Decoder to Scala from Java. Then from Scala to more idiomatic/functional Scala.

Base62Encoder is written using FP and non FP in Go Lang, Java, Scala, Kotlin, JavaScript, TypeScript, Clojure, Rust and Python to demonstrate and discuss FP support in different languages.

These are show notes for this video - Porting Base62Encoder/Decoder from Java to functional Scala

Other videos in this series

Direct port

Main Method

Java Scala
public static void main(String[] args) {
    final long id = 12345678910L;
    final String strId = convertToEncodedString(id);
    final long newId = convertToLong(strId);
    System.out.printf("%d %s %d\n", id, strId, newId);


   final String longURL = "https://www.somewebiste.com/dp/...";
   final long urlId =  Math.abs(longURL.hashCode()); 
   final String shortHandle = convertToEncodedString(urlId);
   System.out.printf("%d %s %d\n", urlId, 
        shortHandle, convertToLong(shortHandle));

}
def main(args: Array[String]): Unit = {
  val id = 12345678910L
  val strId = convertToEncodedString(id)
  val newId = convertToLong(strId)
  printf("%d %s %d\n", id, strId, newId)

  val longURL = "https://www.somewebiste.com/dp/..."
  val urlId = Math.abs(longURL.hashCode)
  val shortHandle = convertToEncodedString(urlId)
  printf("%d %s %d\n", urlId, 
      shortHandle, convertToLong(shortHandle))

}

convertToEncodedString

Java Scala
public static String convertToEncodedString(final long id) {
    final StringBuilder builder = new StringBuilder();
    int placeHolder = findStartBucket(id);
    long bucketValue;
    long acc = id;
    int digitIndex;

    while (placeHolder > 1) {
        //bucketValue = buckets[index];
        bucketValue = powDigitsBase(placeHolder);
        digitIndex = (int) (acc / bucketValue);
        acc = acc - (bucketValue * digitIndex);
        appendSafe(builder, digitIndex);
        placeHolder--;
    }
    //bucketValue = buckets[1];
    bucketValue = powDigitsBase(1);
    digitIndex = (int) (acc / bucketValue);
    acc = acc - (bucketValue * digitIndex);
    appendSafe(builder, digitIndex);

    //Put the remainder in the ones column
    digitIndex = (int) (acc % bucketValue);
    builder.append(DIGITS[digitIndex]);
    return builder.toString();
}
def convertToEncodedString(id: Long): String = {
  val builder = new StringBuilder
  var placeHolder = findStartBucket(id)
  var bucketValue = 0L
  var acc = id
  var digitIndex = 0
  
  while (placeHolder > 1) { 
    //bucketValue = buckets[index];
    bucketValue = powDigitsBase(placeHolder)
    digitIndex = (acc / bucketValue).toInt
    acc = acc - (bucketValue * digitIndex)
    appendSafe(builder, digitIndex)
    placeHolder -= 1
  }
  //bucketValue = buckets[1];
  bucketValue = powDigitsBase(1)
  digitIndex = (acc / bucketValue).toInt
  acc = acc - (bucketValue * digitIndex)
  appendSafe(builder, digitIndex)
  
  //Put the remainder in the ones column
  digitIndex = (acc % bucketValue).toInt
  builder.append(DIGITS(digitIndex))
  builder.toString
}

findStartBucket

Java Scala
private static int findStartBucket(long value) {
   for (int i = 0; i < 15; i++) {
       if (value < powDigitsBase(i)) {
           return i-1;
       }
   }
   return 0;
}
private def findStartBucket(value: Long): Int = {
  for (i <- 0 until 15) {
    if (value < powDigitsBase(i)) return i - 1
  }
  0
}

powDigitsBase

Java Scala
private static long powDigitsBase( final long exponent) {
    return doPow(exponent, DIGITS.length);
}

private static long doPow( final long exponent, final long base) {
    long result = 1;
    long exp = exponent;
    while (exp != 0) {
        result *= base;
        --exp;
    }
    return result;
}
private def powDigitsBase(exponent: Long) = 
             doPow(exponent, DIGITS.length.toLong)

private def doPow(exponent: Long, base: Long) = {
  var result = 1L
  var exp = exponent
  while (exp != 0) {
    result = result * base
    exp -= 1
  }
  result
}

appendSafe

Java Scala
private static void appendSafe(StringBuilder builder, int digitIndex) {
    if (digitIndex != 0) {
        builder.append(DIGITS[digitIndex]);
    } else {
        if (builder.length() > 0) {
            builder.append(DIGITS[digitIndex]);
        }
    }
}
private def appendSafe(builder: StringBuilder, digitIndex: Int): Unit = {
  if (digitIndex != 0) builder.append(DIGITS(digitIndex))
  else if (builder.nonEmpty) builder.append(DIGITS(digitIndex))
}

convertToLong

Java Scala
public static long convertToLong(String strId) {
    return convertToLong(strId.toCharArray());
}
private static long convertToLong(char[] chars) {
    long acc = 0;
    int position = 0;
    for (int index = chars.length - 1; index > -1; index--) {
        char c = chars[index];
        long value =  computeValue(c, position);
        acc += value;
        position++;
    }
    return acc;
}
def convertToLong(strId: String): Long = 
                  convertToLong(strId.toCharArray)

private def convertToLong(chars: Array[Char]) = {
  var acc = 0L
  var position = 0
  for (index <- chars.length - 1 until -1 by -1) {
    val c = chars(index)
    val value = computeValue(c, position)
    acc = acc + value
    position += 1
  }
  acc
}

computeValue

Java Scala
private static long computeValue(char c, int position) {
    final int digitIndex = findIndexOfDigitInTable(c);
    final long multiplier = powDigitsBase(position);
    //final long multiplier = buckets[position];
    return digitIndex * multiplier;
}
private def computeValue(c: Char, position: Int) = {
  val digitIndex = findIndexOfDigitInTable(c)
  val multiplier = powDigitsBase(position)
  digitIndex * multiplier
}

findIndexOfDigitInTable

Java Scala
private static int findIndexOfDigitInTable(char c) {
    for (int i = 0; i < DIGITS.length; i++) {
        char digit = DIGITS[i];
        if (c == digit) {
            return i;
        }
    }
    throw new IllegalStateException("Unknown char #" + c + "#");
}
private def findIndexOfDigitInTable(c: Char): Int = {
  for (i <- 0 until DIGITS.length) {
    val digit = DIGITS(i)
    if (c == digit) return i
  }
  throw new IllegalStateException("Unknown char #" + c + "#")
}

Functional Scala

Main Method

Functional Scala Scala
def main(args: Array[String]): Unit = {
  val id = 12345678910L
  val strId = convertToEncodedString(id)
  val newId = convertToLong(strId)
  println(s"$id $strId $newId")

  val longURL = "https://www.somewebiste.com/dp/..."
  val urlId = Math.abs(longURL.hashCode)
  val shortHandle = convertToEncodedString(urlId)
  println(s"$urlId $shortHandle ${convertToLong(shortHandle)}")

}
def main(args: Array[String]): Unit = {
  val id = 12345678910L
  val strId = convertToEncodedString(id)
  val newId = convertToLong(strId)
  printf("%d %s %d\n", id, strId, newId)

  val longURL = "https://www.somewebiste.com/dp/..."
  val urlId = Math.abs(longURL.hashCode)
  val shortHandle = convertToEncodedString(urlId)
  printf("%d %s %d\n", urlId, 
      shortHandle, convertToLong(shortHandle))

}

convertToEncodedString

Functional Scala Scala
def convertToEncodedString(id: Long): String = {
  val builder: List[String] = List()
  val placeHolder = findStartBucket(id)
  val results = accumulateDigits(placeHolder, id, builder)
  val bucketValue = powDigitsBase(1)
  val digitIndex: Int = (results._2 / bucketValue).toInt
  val acc = results._2 - (bucketValue * digitIndex)
  val newBuilder: List[String] = appendSafeToList(results._3, digitIndex)
  //Put the remainder in the ones column
  val place1DigitIndex = (acc % bucketValue).toInt
  val finalBuilder = newBuilder ++ List(DIGITS(place1DigitIndex).toString)
  finalBuilder.mkString("")
}

private def accumulateDigits(placeHolder: Int, acc: Long, 
                     builder: List[String]): (Int, Long, List[String]) = {
  if (!(placeHolder > 1)) {
    return (placeHolder, acc, builder)
  }
  val bucketValue = powDigitsBase(placeHolder)
  val digitIndex = (acc / bucketValue).toInt
  accumulateDigits(placeHolder - 1, acc - (bucketValue * digitIndex), 
       appendSafeToList(builder, digitIndex))
}
def convertToEncodedString(id: Long): String = {
  val builder = new StringBuilder
  var placeHolder = findStartBucket(id)
  var bucketValue = 0L
  var acc = id
  var digitIndex = 0
  
  while (placeHolder > 1) { 
    //bucketValue = buckets[index];
    bucketValue = powDigitsBase(placeHolder)
    digitIndex = (acc / bucketValue).toInt
    acc = acc - (bucketValue * digitIndex)
    appendSafe(builder, digitIndex)
    placeHolder -= 1
  }
  //bucketValue = buckets[1];
  bucketValue = powDigitsBase(1)
  digitIndex = (acc / bucketValue).toInt
  acc = acc - (bucketValue * digitIndex)
  appendSafe(builder, digitIndex)
  
  //Put the remainder in the ones column
  digitIndex = (acc % bucketValue).toInt
  builder.append(DIGITS(digitIndex))
  builder.toString
}

findStartBucket

Functional Scala Scala
private def findStartBucket(value: Long): Int = {
  val i = Range(0, 15, 1).find(i => value < powDigitsBase(i.toLong))
  i.getOrElse(0)
}
private def findStartBucket(value: Long): Int = {
  for (i <- 0 until 15) {
    if (value < powDigitsBase(i)) return i - 1
  }
  0
}

powDigitsBase

Functional Scala Scala
private def powDigitsBase(exponent: Long): Long = 
                        doPow(exponent, DIGITS.length)

private def doPow(exponent: Long, base: Int): Long = {
  if (exponent == 0) return 1
  doPow(exponent - 1, base) * base
}
private def powDigitsBase(exponent: Long) = 
             doPow(exponent, DIGITS.length.toLong)

private def doPow(exponent: Long, base: Long) = {
  var result = 1L
  var exp = exponent
  while (exp != 0) {
    result = result * base
    exp -= 1
  }
  result
}

appendSafe

Functional Scala Scala
private def appendSafeToList(builder: List[String], digitIndex: Int): List[String] = {
  if (digitIndex != 0) builder ++ List((DIGITS(digitIndex)).toString)
  else if (builder.nonEmpty) builder ++ List((DIGITS(digitIndex)).toString)
  else builder
}
private def appendSafe(builder: StringBuilder, digitIndex: Int): Unit = {
  if (digitIndex != 0) builder.append(DIGITS(digitIndex))
  else if (builder.nonEmpty) builder.append(DIGITS(digitIndex))
}

convertToLong

Functional Scala Scala
def convertToLong(strId: String): Long = 
                            doConvertToLong(strId.toCharArray)

private def doConvertToLong(chars: Array[Char]): Long = {
  val (acc, _) = chars.reverse.foldLeft(0L, 0) { (pos, ch) =>
    val (acc, position) = pos
    val value = computeValue(ch, position)
    (acc + value, position + 1)
  }
  acc
}
def convertToLong(strId: String): Long = 
                  convertToLong(strId.toCharArray)

private def convertToLong(chars: Array[Char]) = {
  var acc = 0L
  var position = 0
  for (index <- chars.length - 1 until -1 by -1) {
    val c = chars(index)
    val value = computeValue(c, position)
    acc = acc + value
    position += 1
  }
  acc
}

computeValue

Functional Scala Scala
private def computeValue(c: Char, position: Int) = {
  val digitIndex = findIndexOfDigitInTable(c)
  val multiplier = powDigitsBase(position)
  digitIndex * multiplier
}
private def computeValue(c: Char, position: Int) = {
  val digitIndex = findIndexOfDigitInTable(c)
  val multiplier = powDigitsBase(position)
  digitIndex * multiplier
}

findIndexOfDigitInTable

Functional Scala Scala
private def findIndexOfDigitInTable(c: Char): Int = {
  val i = DIGITS.indexOf(c);
  if (i == -1) {
    throw new IllegalStateException("Unknown char #" + c + "#")
  }
  i
}
private def findIndexOfDigitInTable(c: Char): Int = {
  for (i <- 0 until DIGITS.length) {
    val digit = DIGITS(i)
    if (c == digit) return i
  }
  throw new IllegalStateException("Unknown char #" + c + "#")
}

Base62Encoder URLShortener

Example URL shortener services:

URL shortener services and Base 62 explained:

URL shortener services and Base 62 tutorials with example code:

First video in this series

Where is Rick?

Java Code example non functional

package main.normal;


public class Base62Encoder {

    public static void main(String[] args) {
        final long id = 12345678910L;
        final String strId = convertToEncodedString(id);
        final long newId = convertToLong(strId);
        System.out.printf("%d %s %d\n", id, strId, newId);


       final String longURL = "https://www.somewebiste.com/dp/0201616165/?_encoding=UTF8&pd_rd_w=vwEcs&content-id=amzn1.sym.8cf3b8ef-6a74-45dc-9f0d-6409eb523603&pf_rd_p=8cf3b8ef-6a74-45dc-9f0d-6409eb523603&pf_rd_r=BQ0KD40K57XG761DBNBA&pd_rd_wg=DtkHk&pd_rd_r=f94b60b7-9080-4065-b77f-6377ec854d17&ref_=pd_gw_ci_mcx_mi";
       final long urlId =  Math.abs(longURL.hashCode()); //or save URL in DB and get ID from DB row.
       final String shortHandle = convertToEncodedString(urlId);
       System.out.printf("%d %s %d\n", urlId, shortHandle, convertToLong(shortHandle));


    }

    private static final char[] DIGITS = {
            //0    1    2    3    4    5    6    7    8    9
            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
            //10    11   12   13  14   15   16   17    18    19   20  21
            'A',    'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
            //22    23   24   25  26   27   28   29    30    31   32  33    34  35
            'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
            // 36  37  38   39   40   41    42    43   44  45   46    47
            'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',           //Easy to add more characters if not using lookup tables.
            // 48  49   50   51   52   53   54   55  56   57   58  59   60   61   // 62   63, 64
            'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', // '*', '@', '$'
    };

    public static long convertToLong(String strId) {
        return convertToLong(strId.toCharArray());
    }

    public static String convertToEncodedString(final long id) {
        final StringBuilder builder = new StringBuilder();
        int placeHolder = findStartBucket(id);
        long bucketValue;
        long acc = id;
        int digitIndex;

        while (placeHolder > 1) {
            //bucketValue = buckets[index];
            bucketValue = powDigitsBase(placeHolder);
            digitIndex = (int) (acc / bucketValue);
            acc = acc - (bucketValue * digitIndex);
            appendSafe(builder, digitIndex);
            placeHolder--;
        }
        //bucketValue = buckets[1];
        bucketValue = powDigitsBase(1);
        digitIndex = (int) (acc / bucketValue);
        acc = acc - (bucketValue * digitIndex);
        appendSafe(builder, digitIndex);

        //Put the remainder in the ones column
        digitIndex = (int) (acc % bucketValue);
        builder.append(DIGITS[digitIndex]);
        return builder.toString();
    }



    private static long convertToLong(char[] chars) {
        long acc = 0;
        int position = 0;
        for (int index = chars.length - 1; index > -1; index--) {
            char c = chars[index];
            long value =  computeValue(c, position);
            acc += value;
            position++;
        }
        return acc;
    }

    private static int findIndexOfDigitInTable(char c) {
        for (int i = 0; i < DIGITS.length; i++) {
            char digit = DIGITS[i];
            if (c == digit) {
                return i;
            }
        }
        throw new IllegalStateException("Unknown char #" + c + "#");
    }

    private static long computeValue(char c, int position) {
        final int digitIndex = findIndexOfDigitInTable(c);
        final long multiplier = powDigitsBase(position);
        //final long multiplier = buckets[position];
        return digitIndex * multiplier;
    }


    private static void appendSafe(StringBuilder builder, int digitIndex) {
        if (digitIndex != 0) {
            builder.append(DIGITS[digitIndex]);
        } else {
            if (builder.length() > 0) {
                builder.append(DIGITS[digitIndex]);
            }
        }
    }

    private static long powDigitsBase( final long exponent) {
        return doPow(exponent, DIGITS.length);
    }

    private static long doPow( final long exponent, final long base) {
        long result = 1;
        long exp = exponent;
        while (exp != 0) {
            result *= base;
            --exp;
        }
        return result;
    }

    private static int findStartBucket(long value) {
       for (int i = 0; i < 15; i++) {
           if (value < powDigitsBase(i)) {
               return i-1;
           }
       }
       return 0;
    }


}
package main.normal;
public class Base62Encoder {
private static final char[] DIGITS = {
//0 1 2 3 4 5 6 7 8 9
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
//10 11 12 13 14 15 16 17 18 19 20 21
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
//22 23 24 25 26 27 28 29 30 31 32 33 34 35
'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
// 36 37 38 39 40 41 42 43 44 45 46 47
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', //Easy to add more characters if not using lookup tables.
// 48 49 50 51 52 53 54 55 56 57 58 59 60 61 // 62 63, 64
'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', // '*', '@', '$'
};
public static void main(String[] args) {
final long id = 12345678910L;
final String strId = convertToEncodedString(id);
final long newId = convertToLong(strId);
System.out.printf("%d %s %d\n", id, strId, newId);
final String longURL = "https://www.somewebiste.com/dp/0201616165/?_encoding=UTF8&pd_rd_w=vwEcs&content-id=amzn1.sym.8cf3b8ef-6a74-45dc-9f0d-6409eb523603&pf_rd_p=8cf3b8ef-6a74-45dc-9f0d-6409eb523603&pf_rd_r=BQ0KD40K57XG761DBNBA&pd_rd_wg=DtkHk&pd_rd_r=f94b60b7-9080-4065-b77f-6377ec854d17&ref_=pd_gw_ci_mcx_mi";
final long urlId = Math.abs(longURL.hashCode()); //or save URL in DB and get ID from DB row.
final String shortHandle = convertToEncodedString(urlId);
System.out.printf("%d %s %d\n", urlId, shortHandle, convertToLong(shortHandle));
}
public static long convertToLong(String strId) {
return convertToLong(strId.toCharArray());
}
private static long convertToLong(char[] chars) {
long acc = 0;
int position = 0;
for (int index = chars.length - 1; index > -1; index--) {
char c = chars[index];
long value = computeValue(c, position);
acc += value;
position++;
}
return acc;
}
public static String convertToEncodedString(final long id) {
final StringBuilder builder = new StringBuilder();
int placeHolder = findStartBucket(id);
long bucketValue;
long acc = id;
int digitIndex;
while (placeHolder > 1) {
//bucketValue = buckets[index];
bucketValue = powDigitsBase(placeHolder);
digitIndex = (int) (acc / bucketValue);
acc = acc - (bucketValue * digitIndex);
appendSafe(builder, digitIndex);
placeHolder--;
}
//bucketValue = buckets[1];
bucketValue = powDigitsBase(1);
digitIndex = (int) (acc / bucketValue);
acc = acc - (bucketValue * digitIndex);
appendSafe(builder, digitIndex);
//Put the remainder in the ones column
digitIndex = (int) (acc % bucketValue);
builder.append(DIGITS[digitIndex]);
return builder.toString();
}
private static int findIndexOfDigitInTable(char c) {
for (int i = 0; i < DIGITS.length; i++) {
char digit = DIGITS[i];
if (c == digit) {
return i;
}
}
throw new IllegalStateException("Unknown char #" + c + "#");
}
private static long computeValue(char c, int position) {
final int digitIndex = findIndexOfDigitInTable(c);
final long multiplier = powDigitsBase(position);
//final long multiplier = buckets[position];
return digitIndex * multiplier;
}
private static void appendSafe(StringBuilder builder, int digitIndex) {
if (digitIndex != 0) {
builder.append(DIGITS[digitIndex]);
} else {
if (builder.length() > 0) {
builder.append(DIGITS[digitIndex]);
}
}
}
private static long powDigitsBase(final long exponent) {
return doPow(exponent, DIGITS.length);
}
private static long doPow(final long exponent, final long base) {
long result = 1;
long exp = exponent;
while (exp != 0) {
result *= base;
--exp;
}
return result;
}
private static int findStartBucket(long value) {
for (int i = 0; i < 15; i++) {
if (value < powDigitsBase(i)) {
return i - 1;
}
}
return 0;
}
}
package nonfunctional
object Main {
private val DIGITS = Array(
//0 1 2 3 4 5 6 7 8 9
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
//10 11 12 13 14 15 16 17 18 19 20 21
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
//22 23 24 25 26 27 28 29 30 31 32 33 34 35
'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
// 36 37 38 39 40 41 42 43 44 45 46 47
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', //Easy to add more characters if not using lookup tables.
// 48 49 50 51 52 53 54 55 56 57 58 59 60 61 // 62 63, 64
'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z') // '*', '@', '$'
def main(args: Array[String]): Unit = {
val id = 12345678910L
val strId = convertToEncodedString(id)
val newId = convertToLong(strId)
printf("%d %s %d\n", id, strId, newId)
val longURL = "https://www.somewebiste.com/dp/0201616165/?_encoding=UTF8&pd_rd_w=vwEcs&content-id=amzn1.sym.8cf3b8ef-6a74-45dc-9f0d-6409eb523603&pf_rd_p=8cf3b8ef-6a74-45dc-9f0d-6409eb523603&pf_rd_r=BQ0KD40K57XG761DBNBA&pd_rd_wg=DtkHk&pd_rd_r=f94b60b7-9080-4065-b77f-6377ec854d17&ref_=pd_gw_ci_mcx_mi"
val urlId = Math.abs(longURL.hashCode) //or save URL in DB and get ID from DB row.
val shortHandle = convertToEncodedString(urlId)
printf("%d %s %d\n", urlId, shortHandle, convertToLong(shortHandle))
}
def convertToLong(strId: String): Long = convertToLong(strId.toCharArray)
private def convertToLong(chars: Array[Char]) = {
var acc = 0L
var position = 0
for (index <- chars.length - 1 until -1 by -1) {
val c = chars(index)
val value = computeValue(c, position)
acc = acc + value
position += 1
}
acc
}
def convertToEncodedString(id: Long): String = {
val builder = new StringBuilder
var placeHolder = findStartBucket(id)
var bucketValue = 0L
var acc = id
var digitIndex = 0
while (placeHolder > 1) {
//bucketValue = buckets[index];
bucketValue = powDigitsBase(placeHolder)
digitIndex = (acc / bucketValue).toInt
acc = acc - (bucketValue * digitIndex)
appendSafe(builder, digitIndex)
placeHolder -= 1
}
//bucketValue = buckets[1];
bucketValue = powDigitsBase(1)
digitIndex = (acc / bucketValue).toInt
acc = acc - (bucketValue * digitIndex)
appendSafe(builder, digitIndex)
//Put the remainder in the ones column
digitIndex = (acc % bucketValue).toInt
builder.append(DIGITS(digitIndex))
builder.toString
}
private def findIndexOfDigitInTable(c: Char): Int = {
for (i <- 0 until DIGITS.length) {
val digit = DIGITS(i)
if (c == digit) return i
}
throw new IllegalStateException("Unknown char #" + c + "#")
}
private def computeValue(c: Char, position: Int) = {
val digitIndex = findIndexOfDigitInTable(c)
val multiplier = powDigitsBase(position)
digitIndex * multiplier
}
private def appendSafe(builder: StringBuilder, digitIndex: Int): Unit = {
if (digitIndex != 0) builder.append(DIGITS(digitIndex))
else if (builder.nonEmpty) builder.append(DIGITS(digitIndex))
}
private def powDigitsBase(exponent: Long) = doPow(exponent, DIGITS.length.toLong)
private def doPow(exponent: Long, base: Long) = {
var result = 1L
var exp = exponent
while (exp != 0) {
result = result * base
exp -= 1
}
result
}
private def findStartBucket(value: Long): Int = {
for (i <- 0 until 15) {
if (value < powDigitsBase(i)) return i - 1
}
0
}
}
package functional
object Main {
private val DIGITS = Array(
//0 1 2 3 4 5 6 7 8 9
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
//10 11 12 13 14 15 16 17 18 19 20 21
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
//22 23 24 25 26 27 28 29 30 31 32 33 34 35
'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
// 36 37 38 39 40 41 42 43 44 45 46 47
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', //Easy to add more characters if not using lookup tables.
// 48 49 50 51 52 53 54 55 56 57 58 59 60 61 // 62 63, 64
'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z') // '*', '@', '$'
def main(args: Array[String]): Unit = {
val id = 12345678910L
val strId = convertToEncodedString(id)
val newId = convertToLong(strId)
println(s"$id $strId $newId")
val longURL = "https://www.somewebiste.com/dp/0201616165/?_encoding=UTF8&pd_rd_w=vwEcs&content-id=amzn1.sym.8cf3b8ef-6a74-45dc-9f0d-6409eb523603&pf_rd_p=8cf3b8ef-6a74-45dc-9f0d-6409eb523603&pf_rd_r=BQ0KD40K57XG761DBNBA&pd_rd_wg=DtkHk&pd_rd_r=f94b60b7-9080-4065-b77f-6377ec854d17&ref_=pd_gw_ci_mcx_mi"
val urlId = Math.abs(longURL.hashCode) //or save URL in DB and get ID from DB row.
val shortHandle = convertToEncodedString(urlId)
println(s"$urlId $shortHandle ${convertToLong(shortHandle)}")
}
def convertToEncodedString(id: Long): String = {
val builder: List[String] = List()
val placeHolder = findStartBucket(id)
val results = accumulateDigits(placeHolder, id, builder)
val bucketValue = powDigitsBase(1)
val digitIndex: Int = (results._2 / bucketValue).toInt
val acc = results._2 - (bucketValue * digitIndex)
val newBuilder: List[String] = appendSafeToList(results._3, digitIndex)
//Put the remainder in the ones column
val place1DigitIndex = (acc % bucketValue).toInt
val finalBuilder = newBuilder ++ List(DIGITS(place1DigitIndex).toString)
finalBuilder.mkString("")
}
private def accumulateDigits(placeHolder: Int, acc: Long, builder: List[String]): (Int, Long, List[String]) = {
if (!(placeHolder > 1)) {
return (placeHolder, acc, builder)
}
val bucketValue = powDigitsBase(placeHolder)
val digitIndex = (acc / bucketValue).toInt
accumulateDigits(placeHolder - 1, acc - (bucketValue * digitIndex), appendSafeToList(builder, digitIndex))
}
def convertToLong(strId: String): Long = doConvertToLong(strId.toCharArray)
private def doConvertToLong(chars: Array[Char]): Long = {
val (acc, _) = chars.reverse.foldLeft(0L, 0) { (pos, ch) =>
val (acc, position) = pos
val value = computeValue(ch, position)
(acc + value, position + 1)
}
acc
}
private def computeValue(c: Char, position: Int) = {
val digitIndex = findIndexOfDigitInTable(c)
val multiplier = powDigitsBase(position)
digitIndex * multiplier
}
private def findIndexOfDigitInTable(c: Char): Int = {
val i = DIGITS.indexOf(c);
if (i == -1) {
throw new IllegalStateException("Unknown char #" + c + "#")
}
i
}
private def appendSafeToList(builder: List[String], digitIndex: Int): List[String] = {
if (digitIndex != 0) builder ++ List((DIGITS(digitIndex)).toString)
else if (builder.nonEmpty) builder ++ List((DIGITS(digitIndex)).toString)
else builder
}
private def powDigitsBase(exponent: Long): Long = doPow(exponent, DIGITS.length)
private def doPow(exponent: Long, base: Int): Long = {
if (exponent == 0) return 1
doPow(exponent - 1, base) * base
}
private def findStartBucket(value: Long): Int = {
val i = Range(0, 15, 1).find(i => value < powDigitsBase(i.toLong))
i.getOrElse(0)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment