1

I was required to make a method to convert integer from base ten to some another base in JavaScript, and it should also support providing your custom digits array. For example,

toBase(10, 2 ["A","B"])// returns 'BABA'

and if digits array is not provided, it should work as JavaScript 'toString' method

var a = 10;
a.toString(2);//returns '1010'

I have wrote a function to convert an integer to another base from base 10 number, with an option of providing digits array -

function toBase(number, radix, digits)  
{
    radix = radix || 10;
    digits = digits || "0123456789abcdefghijklmnopqrstuvwxyz".split("").slice(0, radix)

    if (radix > digits.length) {
        var msg = "Not enough digits to represent the number '" + number + "' in base " + radix;
        throw Error(msg);
    }

    if (number === 0) return digits[0];
    var a = []
    while (number) {
        a.splice(0, 0, digits[number % radix])
        number = parseInt(number / radix);
    }
    return a.join("");
}

This function works fine for me, but I want to know if is there any better way to do it? Thanks.

Moazzam Khan
  • 3,130
  • 2
  • 20
  • 35
  • The `slice` after the `split` is unnecessary, and instead of `splice(0, 0, x)` you should use `unshift(x)`. – Bergi Jan 30 '13 at 13:41

2 Answers2

2

You can just use the native toString method and then replace the output with those from the digits array:

function toBase(number, radix, digits) {
    if (digits && digits.length >= radix)
        return number.toString(radix).replace(/./g, function(d) {
            return digits[ parseInt(d, radix) ];
        });
    else
        return number.toString(radix);
}
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
1

A method that might be slightly faster than the way you have is to bit shift. This works most easily when radix is a power of 2, here is an example

function toBase(x, radix, A) {
    var r = 1, i = 0, s = '';
    radix || (radix = 10); // case no radix
    A || (A = '0123456789abcdefghijklmnopqrstuvwxyz'.split('')); // case no alphabet
    if (A.length < radix) throw new RangeError('alphabet smaller than radix');
    if (radix < 2) throw new RangeError('radix argument must be at least 2');
    if (radix < 37) return useBergisMethod(x, radix, A); // this is arguably one of the fastest ways as it uses native `.toString`
    if (x === 0) return A[0]; // short circuit 0
    // test if radix is a power of 2
    while (radix > r) {
        r = r * 2;
        i = i + 1;
    }
    if (r === radix) { // radix = 2 ^ i; fast method
        r = r - 1; // Math.pow(2, i) - 1;
        while (x > 0) {
            s = A[x & r] + s;
            x >>= i; // shift binary
        }
        return s; // done
    }
    return methodInOriginalQuestion(x, radix, A); // else not a power of 2, slower method
}
/*
toBase(74651278, 64, '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzáé');
"4SnQE"
    // check reverse
var i, j = 0, s = '4SnQE', a = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzáé';
for (i = 0; i < s.length; ++i) j *= 64, j += a.indexOf(s[i]);
j; // 74651278, correct
*/
Paul S.
  • 64,864
  • 9
  • 122
  • 138
  • Not sure whether my method is "arguably faster", it uses about `log_radix(x)` function invocations in the replace… It was just shorter. Have you tested it? – Bergi Jan 30 '13 at 17:36
  • I didn't test it, no, but I'm pretty confident as `number % radix` requires a division, `number / radix` is a division, meaning that a lot of effort is required per character. My code is by no means optimised. – Paul S. Jan 30 '13 at 18:10
  • @Bergi yours could probably be sped up by using an object like `o = {"0":0,"1":1,"2":2,"3":3,"4":4,"5":5,"6":6,"7":7,"8":8,"9":9,"a":10,"b":11,"c":12,"d":13,"e":14,"f":15,"g":16,"h":17,"i":18,"j":19,"k":20,"l":21,"m":22,"n":23,"o":24,"p":25,"q":26,"r":27,"s":28,"t":29,"u":30,"v":31,"w":32,"x":33,"y":34,"z":35}` and looping rather than `.replace` doing `string2 += alphabet[o[string[i]]];` – Paul S. Jan 30 '13 at 18:40
  • Yeah, though I optimized for readability/maintainability and not for speed/efficiency. Especially I did not want to hardcode that object (or any other map representation, like the OPs splitted string). – Bergi Jan 30 '13 at 18:45
  • Well you could avoid that object using `string2 += alphabet[parseInt(string[i], r)];` some `r` where `radix <= r <= 36` and still loop it maintaining readability, but using such an object is [**twice as fast**](http://jsperf.com/parseint-vs-object) – Paul S. Jan 30 '13 at 19:16