Custom Number Base Converter

30

The powers that be want to be able to quickly convert any number they have into their own number base using any format they would like.

Input

Your program must accept 3 parameters.

  1. Number: The string number to be converted
  2. InputFormat: the base string the number is currently in
  3. OutputFormat: the base string that the number is to be converted to.

Output

Your program must convert the Number from the old number base InputFormat to the new number base OutputFormat

Examples

("1","0123456789","9876543210") = "8"
("985724","9876543210","0123456789ABCDEF") = "37C3"
("FF","0123456789ABCDEF","0123456789") = "255"
("FF","0123456789ABCDEF","01234567") = "377"
("18457184548971248772157", "0123456789","Aa0Bb1Cc2Dd3Ee4Ff5Gg6Hh7Ii8Jj9Kk,Ll.Mm[Nn]Oo@Pp#Qq}Rr{Ss-Tt+Uu=Vv_Ww!Xx%Yy*Zz") = ",sekYFg_fdXb"

Additional

The new base 77 test is not required props if it works though

  1. if your in a language where you have to convert to a number first and are locked within 32Bit you can skip it.
  2. as it's an additional test.

All examples were generated by PHP 7.2 with the bcmath extension using the following code (vars mins but code formatted). there will probably be a shorter way this is just the way I came up with for the system I needed to do this with would be nice to see if anyone could come up with a shorter version though.

PHP 7.2 (bcmath - extension) 614 bytes

<?php
function f($a, $b, $c)
{
    $d= str_split($b,1);
    $e= str_split($c,1);
    $f= str_split($a,1);
    $g=strlen($b);
    $h=strlen($c);
    $k=strlen($a);
    $r='';
    if ($c== '0123456789')
    {
        $r=0;
        for ($i = 1;$i <= $k; $i++)
            $retval = bcadd($retval, bcmul(array_search($f[$i-1], $d),bcpow($g,$k-$i)));
        return $r;
    }
    if ($b!= '0123456789')
        $l=f($a, $b, '0123456789');
    else
        $l= $a;
    if ($l<strlen($c))
        return $e[$l];
    while($l!= '0')
    {
        $r= $e[bcmod($l,$h)].$r;
        $l= bcdiv($l,$h,0);
    }
    return $r;
}

Try it Online

Scoring

This is code golf; shortest code wins. Standard loopholes apply.

Martin Barker

Posted 2018-08-16T15:37:14.347

Reputation: 413

How are bigger digits i.e 50,73 denoted? – Windmill Cookies – 2018-08-16T15:40:52.990

5@WindmillCookies By whatever characters are in the format strings. – Adám – 2018-08-16T15:43:07.670

1Oh wait. I had understood the question completely wrong, now understood correctly, thanks! – Windmill Cookies – 2018-08-16T15:43:58.090

6Nice first question! :-) – Giuseppe – 2018-08-16T15:49:23.480

@Giuseppe Thanks believe it or not the reason for this is that have just had to do something that involved converting to a custom number base (40) for work, and thought this would be an awesome code gold question :D – Martin Barker – 2018-08-16T15:52:41.280

2Closely related. – AdmBorkBork – 2018-08-16T15:57:52.913

2It may be worth adding a test case for a "unique" base -- e.g. ["zX", "tXdsyqzSDRP02", "brFNC02bc"] => "cb". (or whatever that should actually be, if that's incorrect) – Fund Monica's Lawsuit – 2018-08-16T23:01:34.153

2I'd suggest a test case with more than 36 characters in the formats, to catch anyone using built-ins that only go up to base 36 – Jo King – 2018-08-17T00:21:29.700

1Are these positive numbers? Nonnegative? – Jakob – 2018-08-20T22:02:00.340

@Jakob none Signed numbers so positive because the work on string's if you will know if you see a - in front of it it's the negative and the programe does not need to do that. – Martin Barker – 2018-08-20T22:41:29.430

So zero doesn't need to be supported? – Jakob – 2018-08-20T22:42:48.013

no Zero exists in none signed numbers, look at the test cases the first one shows how a 0 get's used in decimal to reverse decimal – Martin Barker – 2018-08-20T22:45:29.930

1@JoKing i have added a decimal to base 77 test case :D – Martin Barker – 2018-08-20T22:51:26.020

@MartinBarker I meant can the input number be zero (e.g. 0 in the base with digits 0123456789)? – Jakob – 2018-08-22T18:39:51.323

1@Jakob oh no it does not have to work with 0. – Martin Barker – 2018-08-23T10:22:30.860

Answers

13

sundar - Reinstate Monica

Posted 2018-08-16T15:37:14.347

Reputation: 5 296

.......................you know, I saw that Za did base conversion but the docs on matl.suever weren't clear that it accepted the base's characters, so I didn't try it. RIP me! – Giuseppe – 2018-08-16T19:13:05.070

@Giuseppe Haha, I remembered it only because it seemed like a command that's ripe for (ab)use in some clever hack or two. Ironic that my first use of it is as a straight-up builtin answer. :) – sundar - Reinstate Monica – 2018-08-16T19:29:42.027

1My first thought when I saw "Za" was "lord, Harry Dresden". +1. – Fund Monica's Lawsuit – 2018-08-16T22:57:22.873

8

R, 124 bytes

function(n,s,t,T=L(t),N=(match(!n,!s)-1)%*%L(s)^(L(n):1-1))intToUtf8((!t)[N%/%T^rev(0:log(N,T))%%T+1])
"!"=utf8ToInt
L=nchar

Try it online!

Ugh, this was a doozy. I use the typical base conversion tricks for R, but string manipulations in R are still messy!

Giuseppe

Posted 2018-08-16T15:37:14.347

Reputation: 21 077

Unfortunately this won't work with n="0"... you should add 2 bytes doing log(N+1,T) but causing a leading zero sometimes e.g. when you convert 31 from base 10 to base 2 :( – digEmAll – 2018-08-19T10:18:50.943

To avoid the "zero issue" on logarithm without leading zeros, I don't see a lot of other solutions... you could do log(N+!N,T) of course using ! with the original meaning – digEmAll – 2018-08-19T10:23:24.157

@digEmAll the OP's comments are still a bit unclear, but it seems that zero need not be supported. – Giuseppe – 2018-08-21T14:27:29.033

Oh well.. that's fine then :) – digEmAll – 2018-08-21T14:35:44.767

7

APL (Dyalog Unicode), 22 bytes

Anonymous infix lambda. Takes InputFormat as left argument and OutputFormat as right argument, and prompts for Number from stdin. Assumes ⎕IO (Index Origin) to be 0, which is default on many systems.

{⍵[(≢⍵)⊥⍣¯1⊢(≢⍺)⊥⍺⍳⎕]}

Try it online!

{} "dfn"; is left argument, is right argument
(mnemonic: left and right ends of the Greek alphabet)

⍵[] index the output format with the following:

   prompt for input

  ⍺⍳ɩndices of those characters in the input format

  ()⊥ evaluate as being in the following base:

   ≢⍺ the length of the input format

   yield that (separates ¯1 from (≢⍺))

  ()⊥⍣¯1 convert to the following base:

  ≢⍺ the length of the output format

Adám

Posted 2018-08-16T15:37:14.347

Reputation: 37 779

7

Japt, 5 bytes

Easing back into golf after a 2 week break

nV sW

Try it


Explanation

           :Implicit input of U=Number, V=InputFormat & W=OutputFormat
 nV        :Convert U from base V to decimal
    sW     :Convert to base W string

Shaggy

Posted 2018-08-16T15:37:14.347

Reputation: 24 623

7

C (gcc), 79 + 46 = 125 bytes

char*O;l,n;g(n){n/l&&g(n/l);write(1,O+n%l,1);}

This must be compiled with the

-Df(s,i,o)=for(n=l=0;n=n*strlen(i)+index(i,s[l])-i,s[++l];);l=strlen(O=o);g(n)

flag. (Yes, this is incredibly sketchy, which is why I'm keeping my old answer below.) This defines a macro f that outputs the answer to STDOUT.

Try it online!

C (gcc), 133 131 bytes

char*O;l;g(n){n/l&&g(n/l);write(1,O+n%l,1);}f(s,i,o,n)char*s,*i,*o;{for(n=0,l=strlen(O=o);n=n*strlen(i)+index(i,*s)-i,*++s;);g(n);}

Try it online!

This defines a function f that outputs the answer to STDOUT.

char*O;           // declare variable to store output charset
l;                // will be set to length of O
g(n){             // helper function to print the result
  n/l&&g(n/l);    // recursively calls itself if there are more digits
  write(1,        // output to stdout...
   O+n%l,1);      // the byte at (n mod output base) in O
}
f(s,i,o,n)        // main function
char*s,*i,*o;{    // declare string inputs
for(n=0,          // initialize n to 0
l=strlen(O=o);    // assign output charset so we don't have to pass it to g
n=n*strlen(i)     // repeatedly multiply n by input base...
+index(i,*s)-i,   // ... add the index of the digit in input charset...
*++s;);           // and move to the next digit until there's none left
g(n);             // call the helper function on the resulting integer
}

Doorknob

Posted 2018-08-16T15:37:14.347

Reputation: 68 138

You can save 2 bytes by using putchar instead of write and changing the decoding loop slightly: Try it online!

– ErikF – 2018-08-17T01:04:13.513

This index function saved me one byte as well with my approach, didn't know about it ;) – Felix Palmen – 2018-08-17T10:42:17.327

6

05AB1E, 5 bytes

ÅβIÅв

Try it online!

This does not work in the legacy version of 05AB1E. It only works on the new version, the Elixir rewrite.

How it works

ÅβIÅв – Full program.
Åβ    – Convert from custom base to decimal.
  I   – Push the third input.
   Åв – Convert from decimal to custom base. 

Mr. Xcoder

Posted 2018-08-16T15:37:14.347

Reputation: 39 774

You state it only works in 05AB1E v2 (not sure if that's the correct version number..), but you still provided a TIO-link. Is the Elixir version already on TIO?! :S Or it works for most test cases, but there are some edge cases where it only works in the new version? – Kevin Cruijssen – 2018-08-17T06:48:05.963

205AB1E v2 is now available on TIO. 05AB1E (legacy) (search it in the tio bar) is the name of the old 05AB1E and 05AB1E is the name of the new one. I know you've already seen that in the chatroom, though, but I'll leave it here as a reference for other users. – Mr. Xcoder – 2018-08-17T08:17:11.457

5

MATL, 5 bytes

sundar found the real builtin to do this! Go upvote that answer instead of my dumb one :-(

ZAwYA

Try it online!

          % implicit input N, the number, and S, the digits of the Source base
ZA        % base2dec, convert string N using S as digits into a base 10 integer
w         % swap stack elements, with implicit input T, the digits of the Target base
YA        % dec2base, reverse the ZA operation with digits coming from T instead.

Giuseppe

Posted 2018-08-16T15:37:14.347

Reputation: 21 077

4

Charcoal, 5 bytes

⍘⍘SSS

Try it online! Link is to verbose version of code. Explanation:

  S     Input the "number"
   S    Input the input format
 ⍘      Convert to number using that format
    S   Input the output format
⍘       Convert to string using that format
        Implicitly print

The BaseString function automatically converts between number and string depending on the type of the first parameter.

Neil

Posted 2018-08-16T15:37:14.347

Reputation: 95 035

3

Python 2, 132 129 122 121 bytes

lambda n,a,b:g(sum(len(a)**i*a.find(j)for i,j in enumerate(n[::-1])),b)
g=lambda n,c:c[n:n+1]or g(n/len(c),c)+c[n%len(c)]

Try it online!

An anonymous function (thanks, Erik the Outgolfer!) which converts the original number to a base 10 integer, then passes the integer and the new base string to function g(), which recursively converts to the new base. Now passes length of the OutputFormat as a parameter to g().

Updated g() for a lower bytecount. (thanks, Dennis!)

Replaced index() with find(). (thanks, Mr. Xcoder!)

Ungolfed Explanation:

def f(n, a, b):
    # reverse the string to that the least significant place is leftmost
    # Ex: 985724 -> 427589
    n = n[::-1]
    # get the value of each place, which is its index in the InputFormat, times the base to the power of the place
    # Ex: 427589, 9876543210 -> 5*10^0, 7*10^1, 2*10^2, 4*10^3, 1*10^4, 0*10^5 -> [5,70,200,4000,10000,0]
    n = [a.find(j)*len(a)**i for i,j in enumerate(n)]
    # add all of the values together to bet the value in base 10
    # Ex: (5 + 70 + 200 + 4000 + 10000 + 0) = 14275
    n = sum(n)

    # call the convert to base function
    return g(n, b)

def g(n, c):
    # string slice, which will return an empty string if n:n+1 is not in range
    # an empty string is falsey
    if c[n:n+1]:
        return c[n:n+1]
    else:
        # get current least significant digit
        rem = c[n%len(c)]
        # get the rest of the integer
        div = n/len(c)

        # get the converted string for the rest of the integer, append the calculated least significant digit
        return g(div,c)+rem

Triggernometry

Posted 2018-08-16T15:37:14.347

Reputation: 765

1You don't need f=, anonymous functions are allowed by default. – Erik the Outgolfer – 2018-08-16T16:39:20.950

@Erik the Outgolfer Is that allowed when the anonymous function calls another function, tho? – Triggernometry – 2018-08-16T16:43:22.910

As long as you include the other stuff in your bytecount, yes, you're allowed to define variables and import modules. – Erik the Outgolfer – 2018-08-16T16:44:53.223

1The helper function can become g=lambda n,c:c[n:n+1]or g(n/len(c),c)+c[n%len(c)]. – Dennis – 2018-08-16T16:56:04.793

1And the main one can become lambda n,a,b:g(sum(len(a)**i*a.find(j)for i,j in enumerate(n[::-1])),b,len(b)). – Mr. Xcoder – 2018-08-16T17:10:19.883

2

Jelly, 11 bytes

iⱮ’ḅL{ṃ⁵ṙ1¤

Try it online!

Argument order: InputFormat, Number, OutputFormat. Be sure to quote the arguments with proper escaping!

Erik the Outgolfer

Posted 2018-08-16T15:37:14.347

Reputation: 38 134

Hrm not sure I did explicitly state the order of the params... – Martin Barker – 2018-08-16T16:10:32.103

@MartinBarker The params are taken in order 2, 1, 3 here. I can't see a requirement for a specific order in the challenge, and that would be discouraged. – Erik the Outgolfer – 2018-08-16T16:12:14.067

3@MartinBarker Pro Tip: Be flexible with such things. I find the order of the inputs to be completely irrelevant when solving a task, so I suggest that you allow any arbitrarily chosen ordering of the parameters – Mr. Xcoder – 2018-08-16T16:13:10.623

i was gonna let it stick anyways just trying to test it now. – Martin Barker – 2018-08-16T16:14:18.917

2

VBA, 182 bytes

A declared subroutine which takes input, n, in the language y and projects that into language z.

Sub f(n,y,z)
l=Len(n)
For i=-l To-1
v=v+(InStr(1,y,Mid(n,-i,1))-1)*Len(y)^(l+i)
Next
l=Len(z)
While v
v=v-1
d=v Mod l+1
v=v\l
If d<0Then v=v+1:d=d-l
o=Mid(z,d+1,1)&o
Wend
n=o
End Sub

Taylor Scott

Posted 2018-08-16T15:37:14.347

Reputation: 6 709

2

Pyth, 21 bytes

s@LeQjimx@Q1dhQl@Q1le

Test suite

Explanation:
s@LeQjimx@Q1dhQl@Q1le  | Code
s@LeQjimx@Q1dhQl@Q1leQ |  with implicit variables
       m               | Map the function
        x   d          |   index of d in
         @Q1           |    the second string in the input
             hQ        |  over the first string in the input
      i                | Convert the resulting list to int from base
               l@Q1    |  length of the second string in the input
     j                 | Convert the int into a list in base
                   leQ |  length of the last string in the input
 @LeQ                  | Turn each number in the list into the character from the numbers index in the last string in the input
s                      | Concatenate the strings in to one string
                       | Implicit print

hakr14

Posted 2018-08-16T15:37:14.347

Reputation: 1 295

2

Haskell, 119 bytes

n!f=init.((foldl((+).(l f*))0[i|c<-n,(i,d)<-zip[0..]f,d==c],0)#)
(0,d)#b=[b!!d]
(r,d)#b=r`divMod`l b#b++[b!!d]
l=length

Try it online!

ბიმო

Posted 2018-08-16T15:37:14.347

Reputation: 15 345

2

JavaScript (ES6), 90 86 bytes

Takes input as (input_format)(output_format)(number).

s=>d=>g=([c,...n],k=0)=>c?g(n,k*s.length+s.search(c)):k?g(n,k/(l=d.length)|0)+d[k%l]:n

Try it online!

Arnauld

Posted 2018-08-16T15:37:14.347

Reputation: 111 334

Sorry, this is not valid as your changing the input format of the string to an array that is not something that can be done via a CLI input. and has to be programmed you need split the string into the array for the first param for this to be valid. – Martin Barker – 2018-08-20T22:36:20.773

@MartinBarker Which rule are you referring to? Updated to take 3 strings anyway. – Arnauld – 2018-08-21T04:26:27.863

All 3 of the input parameters says "string" as C++ a string can be directly read in and used as an array with javascript it can't be. – Martin Barker – 2018-08-21T17:42:45.437

2

Perl 6, 100 97 bytes

{$^c.comb[(":"~$^b.chars~[$^a.comb>>.&{index $b,$_}].perl).EVAL.polymod($c.chars xx*)].join.flip}

Try it online!

Anonymous code block that takes 3 strings in order, input, input format and output format, then returns a string

Explanation:

{  # Anonymous code block
  $^c.comb[  # Split the output format into characters
           (":"~$^b.chars~[$^a.comb>>.&{index $b,$_}].perl) # The radix syntax in a string e.g ":3[1,2,3]"
           .EVAL  # Eval'ed to produce the base 10 version
           .polymod($c.chars xx*)  # Converted to a list in the output base (reversed)
          ] # Convert the list into indexes of the output format
           .join  # Join the characters to a string
           .flip  # And unreversed
}

Jo King

Posted 2018-08-16T15:37:14.347

Reputation: 38 234

1

Python 2, 97 95 bytes

Thanks to Chas Brown for -2 bytes.

n,s,t=input()
k=0;w='';x=len(t)
for d in n:k=len(s)*k+s.find(d)
while k:w=t[k%x]+w;k/=x
print w

Try it online!

ovs

Posted 2018-08-16T15:37:14.347

Reputation: 21 408

1

C (gcc), 130 129 bytes

v;c(r,i,s,t)char*r,*i,*t;{for(r[1]=v=0;*i;v=v*strlen(s)+index(s,*i++)-s);for(s=strlen(t),i=1;*r=t[v%s],v/=s;memmove(r+1,r,++i));}

Try it online!

-1 byte using index instead of strchr.

This is a simple iterative approach, reusing some variables (and thereby abusing sizeof(int) == sizeof(char *) on TIO) to save bytes.

Input:

  • i input number
  • s source base characters
  • t target base characters

Output:

  • r result number (pointer to a buffer)

Explanation:

v;                                        // value of number
c(r,i,s,t)char*r,*i,*t;{
    for(r[1]=v=0;                         // initialize value and second
                                          // character of output to 0
        *i;                               // loop while not at the end of
                                          // input string
         v=v*strlen(s)+index(s,*i++)-s);  // multiply value with source base
                                          // and add the value of the current
                                          // digit (position in the base string)
    for(s=strlen(t),i=1;                  // initialize s to the length of the
                                          // target base string, length of
                                          // result to 1
        *r=t[v%s],v/=s;                   // add character for current digit
                                          // (value modulo target base) and
                                          // divide value by target base until
                                          // 0 is reached
        memmove(r+1,r,++i));              // move result string one place to
                                          // the right
}

Felix Palmen

Posted 2018-08-16T15:37:14.347

Reputation: 3 866

Suggest bcopy(r,r+1,++i) instead of memmove(r+1,r,++i) – ceilingcat – 2018-08-21T19:14:32.670

1

Java 10, 131 bytes

A lambda taking the parameters in order as strings and returning a string.

(i,f,o)->{int n=0,b=o.length();var r="";for(var c:i.split(r))n=n*f.length()+f.indexOf(c);for(;n>0;n/=b)r=o.charAt(n%b)+r;return r;}

Try It Online

Ungolfed

(i, f, o) -> {
    int n = 0, b = o.length();
    var r = "";
    for (var c : i.split(r))
        n = n * f.length() + f.indexOf(c);
    for (; n > 0; n /= b)
        r = o.charAt(n % b) + r;
    return r;
}

Jakob

Posted 2018-08-16T15:37:14.347

Reputation: 2 428