Skip to content

Instantly share code, notes, and snippets.

@RichardHightower
Last active January 12, 2023 22:55
Show Gist options
  • Save RichardHightower/5b45e5162cf8295f73e71d67ad4a442d to your computer and use it in GitHub Desktop.
Save RichardHightower/5b45e5162cf8295f73e71d67ad4a442d to your computer and use it in GitHub Desktop.
6 Video 6 Show Notes

Video 6 show notes

  • Link to this page: https://bit.ly/rickScalaClojureB62
  • Link to related video

Tags

#clojure #functionalprogramming #scala #base62 

Base62Encoder URLShortener

Example URL shortener services:

URL shortener services and Base 62 explained:

URL shortener services and Base 62 tutorials with example code:

Videos in this series

Related articles

Where is Rick?

Title: Porting Base62Encoder/Decoder from Scala to Clojure

Porting Base62Encoder/Decoder from Scala to Clojure Java.

Link To video

Scala to Clojure

Main Method

Scala Clojure
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)}")

}
 (defn -main
  [& args]
  (printf " %s %d\n %s %d \n"
          (convertToEncodedString 12345678910)
          (convertToLong (convertToEncodedString 12345678910))
          (convertToEncodedString (Math/abs (.hashCode "https://www.somewebiste.com/dp/0201616165...")))
          (Math/abs (.hashCode "https://www.somewebiste/dp/0201616165..."))
  )
) 
  

convertToEncodedString

Scala Clojure
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))
}
(defn convertToEncodedString [id]
    (let  [
            placeHolder (findStartBucket  id)
            results (accumulateDigits placeHolder  id  [] )
            ]
      (let [
             bucketValue (powDigitsBase 1)
             digitIndex (int (/ (nth results 1) bucketValue))
             acc (- (nth results 1) (* bucketValue digitIndex))
             newBuilder (appendSafeToList (nth results 2) digitIndex)
             place1DigitIndex (int (mod acc bucketValue ))
             finalBuilder (conj newBuilder (nth DIGITS place1DigitIndex))
             ]
        (clojure.string/join "" finalBuilder)
      )
    )
)

(defn accumulateDigits [placeHolder, acc, builder]
      (if (not (> placeHolder 1))
        [placeHolder, acc, builder]
        (let  [
               bucketValue (powDigitsBase  placeHolder)
               digitIndex (int (/ acc bucketValue))
               ]
              (accumulateDigits
                (- placeHolder  1)
                (- acc  (* bucketValue  digitIndex) )
                (appendSafeToList builder digitIndex)
                )
              )
        )
)

findStartBucket

Scala Clojure
private def findStartBucket(value: Long): Int = {
  val i = Range(0, 15, 1).find(i => value < powDigitsBase(i.toLong))
  i.getOrElse(0)
}
(defn findStartBucket [value]
  (first (filter  (fn [i] (< value (powDigitsBase i ))) INDEXES))
)

powDigitsBase

Scala Clojure
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
}
(defn powDigitsBase [exponent]
  (doPow exponent (count DIGITS))
)
(defn doPow [exponent, base]
  (if (== exponent  0)  1
    (* (doPow (- exponent 1)  base) base)
  )
)

appendSafeToList

Scala Clojure
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
}
(defn appendSafeToList [builder, digitIndex]
  (if (not (= digitIndex 0))
      (conj
          builder,
           (nth DIGITS digitIndex)
      )
      (if (> (count builder) 0)
        (conj
           builder,
           (nth DIGITS digitIndex)
        )
        builder
      )
  )
)

convertToLong

Scala Clojure
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
}
(defn  convertToLong [strId]
  (nth (doConvertToLong (seq (char-array strId)) ) 0)
)
(defn  doConvertToLong [chars]
       (reduce
         (fn [pos ch]
             (let [
                   acc (nth pos 0)
                   position (nth pos 1)
                   value (computeValue ch position)
                   ]
                  [(+ acc value), (+ position 1)]
                  )
             )
         [(long 0), 0]
         (reverse chars))
) 

computeValue

Scala Clojure
private def computeValue(c: Char, position: Int) = {
  val digitIndex = findIndexOfDigitInTable(c)
  val multiplier = powDigitsBase(position)
  digitIndex * multiplier
}
(defn computeValue [c, position]
      (let [
            digitIndex (clojure.string/index-of DIGITS c)
            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 + "#")
}

Clojure full example

(ns my-project.core
  (:gen-class))


(def DIGITS (clojure.string/join "" [
              0  1   2   3   4   5  6   7   8   9
              'A, 'B 'C 'D 'E 'F 'G 'H 'I 'J 'K 'L
              'M 'N 'O 'P 'Q 'R 'S 'T 'U 'V 'W 'X 'Y 'Z
              'a 'b 'c 'd 'e 'f 'g 'h 'i 'j 'k 'l
              'm 'n 'o 'p 'q 'r 's 't 'u 'v 'w 'x 'y 'z
              ])
)

(def INDEXES ( range (long 0) (long 11) (long 1) ))

(defn doPow [exponent, base]
  (if (== exponent  0)  1
    (* (doPow (- exponent 1)  base) base)
  )
)
(defn powDigitsBase [exponent]
  (doPow exponent (count DIGITS))
)

(defn findStartBucket [value]
  (first (filter  (fn [i] (< value (powDigitsBase i ))) INDEXES))
)

(defn appendSafeToList [builder, digitIndex]
  (if (not (= digitIndex 0))
      (conj
          builder,
           (nth DIGITS digitIndex)
      )
      (if (> (count builder) 0)
        (conj
           builder,
           (nth DIGITS digitIndex)
        )
        builder
      )
  )
)

(defn accumulateDigits [placeHolder, acc, builder]
      (if (not (> placeHolder 1))
        [placeHolder, acc, builder]
        (let  [
               bucketValue (powDigitsBase  placeHolder)
               digitIndex (int (/ acc bucketValue))
               ]
              (accumulateDigits
                (- placeHolder  1)
                (- acc  (* bucketValue  digitIndex) )
                (appendSafeToList builder digitIndex)
                )
              )
        )
)

(defn convertToEncodedString [id]
    (let  [
            placeHolder (findStartBucket  id)
            results (accumulateDigits placeHolder  id  [] )
            ]
      (let [
             bucketValue (powDigitsBase 1)
             digitIndex (int (/ (nth results 1) bucketValue))
             acc (- (nth results 1) (* bucketValue digitIndex))
             newBuilder (appendSafeToList (nth results 2) digitIndex)
             place1DigitIndex (int (mod acc bucketValue ))
             finalBuilder (conj newBuilder (nth DIGITS place1DigitIndex))
             ]
        (clojure.string/join "" finalBuilder)
      )
    )
)


(defn computeValue [c, position]
      (let [
            digitIndex (clojure.string/index-of DIGITS c)
            multiplier (powDigitsBase position)
            ]
           (* digitIndex multiplier)
           )
 )

(defn  doConvertToLong [chars]
       (reduce
         (fn [pos ch]
             (let [
                   acc (nth pos 0)
                   position (nth pos 1)
                   value (computeValue ch position)
                   ]
                  [(+ acc value), (+ position 1)]
                  )
             )
         [(long 0), 0]
         (reverse chars))
       )

(defn  convertToLong [strId]
  (nth (doConvertToLong (seq (char-array strId)) ) 0)
)






(defn -main
  [& args]
  (printf " %s %d\n %s %d \n"
          (convertToEncodedString 12345678910)
          (convertToLong (convertToEncodedString 12345678910))
          (convertToEncodedString (Math/abs (.hashCode "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")))
          (Math/abs (.hashCode "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"))
  )
)

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;
    }


}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment