Skip to content

Instantly share code, notes, and snippets.

@RichardHightower
Last active January 12, 2023 22:52
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/035fda0b65de540574e458dedf9dae6d to your computer and use it in GitHub Desktop.
Save RichardHightower/035fda0b65de540574e458dedf9dae6d to your computer and use it in GitHub Desktop.
Show Notes

Base62Encoder URLShortener

Base62Encoder/Decoder written in non-functional Java

Show notes for this video - Base62Encoder/Decoder written in non-functional Java.

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.

#java #functionaljava #base62eoncoder_decoder

Example URL shortener services:

URL shortener services and Base 62 explained:

URL shortener services and Base 62 tutorials with example code:

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


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