In Salesforce CRM, every object has 15-character alphanumeric ID, which is case-sensitive. If anyone's curious, actually it's base-62 number. However, tools used for data migration and integration may or may not support case sensitivity. To overcome that, IDs can be safely converted to 18-character case-insensitive alphanumeric IDs. In that process 3-character alphanumeric checksum is appended to the ID. The conversion algorithm is:


  1. Split ID into three 5-character chunks.

    a0RE0  00000  IJmcN
  2. Reverse each chunk.

    0ER0a  00000  NcmJI
  3. Replace each character in every chunk by 1 if it's uppercase or by 0 if otherwise.

    01100  00000  10011
  4. For each 5-digit binary number i, get character at position i in concatenation of uppercase alphabet and digits 0-5 (ABCDEFGHIJKLMNOPQRSTUVWXYZ012345).

    00000 -> A,
    00001 -> B,
    00010 -> C, ..., 
    11010 -> Z, 
    11011 -> 0, ...,
    11111 -> 5`


    M  A  T
  5. Append these characters, the checksum, to the original ID.



Write program or function which takes 15-character alphanumeric (ASCII) string as input and returns 18-character ID.

Input validation is out of scope of this question. Programs may return any value or crash on invalid input.

Please, don't use Salesforce propretiary languages' features that make this challenge trivial (such as formula CASESAFEID(), converting Id to String in APEX &c).

Test Cases

a01M00000062mPg    -> a01M00000062mPgIAI
001M000000qfPyS    -> 001M000000qfPySIAU
a0FE000000D6r3F    -> a0FE000000D6r3FMAR
0F9E000000092w2    -> 0F9E000000092w2KAA
aaaaaaaaaaaaaaa    -> aaaaaaaaaaaaaaaAAA
AbCdEfGhIjKlMnO    -> AbCdEfGhIjKlMnOVKV
  • 3
    \$\begingroup\$ Sadly, converting a string to an Id in Apex Code still wouldn't be shorter than some of the answers provided here, especially if the code must be self-contained. Apex Code is not well-suited for golfing. \$\endgroup\$
    – phyrfox
    Commented Jan 12, 2016 at 6:34
  • 2
    \$\begingroup\$ @phyrfox as a former salesforce dev. Apex isn't suited for much... \$\endgroup\$ Commented Jan 12, 2016 at 9:08
  • 2
    \$\begingroup\$ APEX, 56 bytes: public class X{public X(Id i){System.debug((String)i);}}. Works only with valid Salesforce IDs, though. \$\endgroup\$
    – Trang Oul
    Commented Jan 12, 2016 at 11:49
  • \$\begingroup\$ I came here looking to actually do this for work (success.jitterbit.com/display/DOC/…), not golf, but I'm a little confused by the description of the algorithm. You say each reversed-and-sanitized chunk in step 4 will be a "binary number," but you never replace digits 2-8 with 0's and 1's. What exactly am I supposed to do for step 4 when steps 1-3 on a chunk like "62mPg" have resulted in a number like "01026"? \$\endgroup\$
    – k..
    Commented Oct 27, 2017 at 18:04

33 Answers 33


Pyth, 23 22 bytes

1 byte saved by FryAmTheEggman.


Try it online. Test suite.

This might be the first time I've used the print instruction in golfing.


     JrG1                   save uppercase alphabet in J
                     z      input string
                    p       print it without newline
                  c3        split into 3 parts
 m              d           for each part:
               _              reverse
            }R                map characters to being in
              J                 uppercase alphabet (saved in J)
           i     2            parse list of bools as binary
  @                           get correct item of
     J                          uppercase alphabet (saved in J)
   s+    U6                     add nums 0-5 to it
s                           concatenate and print

Ruby, 97 bytes

->s{s+s.scan(/.{5}/).map{|x|[*?A..?Z,*?0..?5][x.reverse.gsub(/./){|y|y=~/[^A-Z]/||1}.to_i 2]}*''}
->s{               # define an anonymous lambda
s+                 # the original string plus...
s.scan(/.{5}/)     # get every group of 5 chars
.map{|x|           # map over each group of 5 chars...
[*?A..?Z,*?0..?5]  # build the array of A-Z0-5
[                  # index over it with...
x.reverse          # the 5-char group, reversed...
.gsub(/./){|y|     # ... with each character replaced with...
y=~/[^A-Z]/||1     # ... whether it's uppercase (0/1)...
}.to_i 2           # ... converted to binary
]                  # (end index)
}*''               # end map, join into a string
}                  # end lambda

This one's got some really neat tricks.

My original instinct for splitting the string into groups of 5 chars was each_slice:

irb(main):001:0> [*1..20].each_slice(5).to_a
=> [[1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12, 13, 14, 15], [16, 17, 18, 19, 20]]

Turns out that's waaay too long compared to a simple regex (x.chars.each_slice(5) vs. x.scan(/.{5}/)). This seems obvious in hindsight, but I never really thought about it... perhaps I can optimize some of my old Ruby answers here.

The thing I'm most proud of in this answer, though, is this piece of code:


Alright, so here's some background for the non-Rubyists. Ruby completely separates booleans (TrueClass, FalseClass) from integers/numbers (Numeric)—that means there's no automatic conversion from true to 1 and false to 0 either. This is annoying while golfing (but a good thing... for all other purposes).

The naïve approach to checking whether a single character is uppercase (and returning 1 or 0) is


We can get this down a little further (again, with a regex):


But then I really started thinking. Hmm... =~ returns the index of a match (so, for our single character, always 0 if there's a match) or nil on failure to match, a falsy value (everything else except FalseClass is truthy in Ruby). The || operator takes its first operand if it's truthy, and its second operand otherwise. Therefore, we can golf this down to


Alright, let's look at what's happening here. If y is an uppercase letter, it will fail to match [^A-Z], so the regex part will return nil. nil || 1 is 1, so uppercase letters become 1. If y is anything but an uppercase letter, the regex part will return 0 (because there's a match at index 0), and since 0 is truthy, 0 || 1 is 0.

... and only after writing all of this out do I realize that this is actually the same length as y=~/[A-Z]/?1:0. Haha, oh well.


MATL, 24 bytes


Uses current version (9.1.0) of the language/compiler.


>> matl
 > j1Y24Y2hG5IePtk=~!XB1+)h
> a0RE000000IJmcN

>> matl
 > j1Y24Y2hG5IePtk=~!XB1+)h
> a01M00000062mPg


j            % input string
1Y2          % predefined literal: 'ABC...Z'
4Y2          % predefined literal; '012...9'
h            % concatenate into string 'ABC...Z012...9'
G            % push input string
5Ie          % reshape into 5x3 matrix, column-major order
P            % flip vertically
tk=~         % 1 if uppercase, 0 if lowercase
!XB1+        % convert each column to binary number and add 1
)            % index 'ABC...Z012...9' with resulting numbers
h            % concatenate result with original string

JavaScript (ES6), 108




// Less golfed

  x.replace(/[A-Z]/g,(x,i)=>t|=1<<i,t=0); // build a 15 bit number (no need to explicit reverse)
  // convert 't' to 3 number of 5 bits each, then to the right char A..Z 0..5
  [0,5,10].forEach(n=> // 3 value for shifting
    x += 'ABCDEFGHIJKLMNOPQRSTUVWXYZ012345' // to convert value to char
     [ t>>n&31 ] // shift and mask
  return x


, ['001M000000qfPyS','001M000000qfPySIAU']
, ['a0FE000000D6r3F','a0FE000000D6r3FMAR']
, ['0F9E000000092w2','0F9E000000092w2KAA']
, ['aaaaaaaaaaaaaaa','aaaaaaaaaaaaaaaAAA']
, ['AbCdEfGhIjKlMnO','AbCdEfGhIjKlMnOVKV']
  var i=t[0],x=t[1],r=f(i);
  console.log(i+'->'+r+(r==x?' OK':' Fail (expected '+x+')'));
<pre id=O></pre>


CJam, 27 bytes


Run all test cases.

A fairly straightforward implementation of the spec. The most interesting part is the conversion to characters in the checksum. We add 17 to the result of each chunk. Take that modulo 43 and add the result of that to the character '0.


Japt, 46 bytes

U+U®f"[A-Z]" ?1:0} f'.p5)®w n2 +A %36 s36 u} q

Not too happy with the length, but I can't find a way to golf it down. Try it online!


JavaScript (ES6), 137 132 bytes


4 bytes saved thanks to @ՊՓԼՃՐՊՃՈԲՍԼ!


This challenge is not suited for JavaScript at all. There's no short way to reverse a string and it looks like the shortest way to convert the number to a character is to hard-code each possible character.

  s+                                   // prepend the original ID
  s.replace(/./g,c=>c>"9"&c<"a")       // convert each upper-case character to 1
  .match(/.{5}/g).map(n=>              // for each group of 5 digits
    [0|"0b"+                            // convert from binary
      [...n].reverse().join``]          // reverse the string

If the digits in the checksum were allowed to be lower-case it could be done in 124 bytes like this:



var solution = s=>s+s.replace(/./g,c=>c>"9"&c<"a").match(/.{5}/g).map(n=>"ABCDEFGHIJKLMNOPQRSTUVWXYZ012345"[0|"0b"+[...n].reverse().join``]).join``
<input type="text" id="input" value="AbCdEfGhIjKlMnO" />
<button onclick="result.textContent=solution(input.value)">Go</button>
<pre id="result"></pre>

  • \$\begingroup\$ If I'm not mistaken, parseInt([...n].reverse().join``,2) could be changed to +`0b${[...n].reverse().join``}`. \$\endgroup\$ Commented Jan 12, 2016 at 2:07
  • \$\begingroup\$ @ՊՓԼՃՐՊՃՈԲՍԼ Right you are! I saved another byte on top of that too, thanks. \$\endgroup\$
    – user81655
    Commented Jan 12, 2016 at 6:54
  • \$\begingroup\$ Save 10 whole bytes by using .replace(/.{5}/g,n=>/*stuff*/). \$\endgroup\$
    – Neil
    Commented Jan 13, 2016 at 17:50

MATLAB, 100 98 bytes


A string will be requested as input and the output will be displayed on the screen.


I'm probably using the most straight-forward approach here:

  • Request input
  • Reshape to 5 (rows) x 3 (columns)
  • Flip the row order
  • Transpose the matrix to prepare it for being read as binary
  • Allocate the ABC...XYZ012345 array
  • Compare the character indices of the transposed matrix to its lower-case equivalent and convert the booleans to strings, which are then read as binary and converted to decimal.
  • Interpret these decimals (incremented by 1) as indices of the allocated array.
  • Display the input with the additional 3 characters

Now below 100 bytes thanks to Luis Mendo!

  • 1
    \$\begingroup\$ You can save a little using e=['A':'Z',48:53] \$\endgroup\$
    – Luis Mendo
    Commented Jan 11, 2016 at 22:15
  • \$\begingroup\$ I see my approach is almost the same as yours :-) \$\endgroup\$
    – Luis Mendo
    Commented Jan 11, 2016 at 22:16

C, 120 118 bytes


Works for any input whose length is a multiple of 5 :)



main(c,v,s) char **v, *s;
    for(printf(s = v[1]); *s; s+=5)
        for(n=0, j=5; j--;)

  • \$\begingroup\$ To save a few bytes you can remove n, from the global namespace if you use main(n,v,s) for your signature since you're not otherwise using argc. \$\endgroup\$
    – cleblanc
    Commented Jan 11, 2016 at 21:49
  • \$\begingroup\$ Also replace 26*17 with plain old 442 saves another byte \$\endgroup\$
    – cleblanc
    Commented Jan 11, 2016 at 21:52
  • \$\begingroup\$ With a few more edits I got your version down to 110 bytes. I don't understand why you had !!isupprer() when isupper() seems to work fine for me. Also I refactored your for loops to remove some unnecessary {} j;main(n,v,s)char**v,*s;{for(printf(s=v[1]);*s;s+=5,putchar(n+65-n/442))for(n=0,j=5;j--;n=n*2+isupper(s[j]));} \$\endgroup\$
    – cleblanc
    Commented Jan 11, 2016 at 22:05
  • 1
    \$\begingroup\$ @cleblanc Excellent suggestions, thanks very much. The order of operations is very important on the n/26*17 expression so replacing with 442 is not an option. As far as !!isupper, that function doesn't return 1 for true on my system, it returns 256. The !! is a short way to convert it to a 0/1 return value no matter what. YMMV. \$\endgroup\$ Commented Jan 12, 2016 at 14:28

PHP, 186 181 bytes

<?$z=$argv[1];$x=str_split($z,5);$l="ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";foreach($x as$y){foreach(str_split(strrev($y))as$a=>$w)$y[$a]=ctype_upper($w)?1:0;$z.=$l[bindec($y)];}echo $z;


$z = $argv[1];
$x = str_split($z,5);
foreach($x as $y) {
    foreach( str_split( strrev($y) ) as $a => $w) {
        $y[$a] = ctype_upper($w) ? 1 : 0;
    $z .= $l[bindec($y)];
echo $z;

I started out thinking I could make it much shorter than this, but I ran out of ideas to make it shorter.


Japt, 21 bytes

;+ò5 ®Ô£BèXÃÍgB¬c6oì

Try it (includes all test cases)

;+ò5 ®Ô£BèXÃÍgB¬c6oì     :Implicit input of string
 +                        :Append
  ò5                      :Partitions of length 5
     ®                    :Map
      Ô                   :   Reverse
       £                  :   Map each X
;       B                 :     Uppercase alphabet
         èX               :     Count occurrences of X
           Ã              :  End map
            Í             :  Convert from binary string to integer
             g            :  Index into
;             B           :    Uppercase alphabet
               ¬          :    Split
                c         :    Concatenate
                 6o       :    Range [0,6)
                   Ã      :End map
                    ¬     :Join

Perl, 80 bytes

$_.=join"",map{(A..Z,0..5)[oct"0b".join"",map{lc cmp$_}split//,reverse]}/.{5}/g

To be run as:

perl -ple '$_.=join"",map{(A..Z,0..5)[oct"0b".join"",map{lc cmp$_}split//,reverse]}/.{5}/g'

Pretty straightforward except lc cmp $_ and oct "0b" . $str for binary conversion.


Jelly, 19 18 17 bytes


Try it online!


Ȯe€ØAs5UḄ‘ịØA;ØD¤   Main monadic link
Ȯ                   Print (without newline)
  €                 Map:
 e                   is element of
   ØA                 the uppercase alphabet
     s              Split into chunks of
      5              five
       U            Reverse [each]
        Ḅ           Convert [each] from binary
         ‘          Add 1 [to each]
          ị         Lookup [each] by 1-based index in
                ¤   (
           ØA        the uppercase alphabet
             ;       join with
              ØD     digits 0-9
                ¤   )

-1 byte by inspiration from ovs' O5AB1E answer, printing the initial string directly instead of joining it at the end

-1 byts because I'm a dumb and didn't realize that U vectorizes


Vyxal, 18 17 bytes


Try it Online!

My first attempt at golfing in Vyxal.


ƛ ;                 Map
 ꜝ                    Is uppercase?
   5ẇ               Split into chunks of 5
     R              Reverse each
      v             Vectorize
       B              Convert from binary
        kAkd+       Push uppercase letters joined with digits
             $      Swap
              İ     Index
               ṅ    Join into a string
                +   Append to the original string

Python 2, 97 bytes

lambda i:i+''.join(chr(48+(17+sum((2**j)*i[x+j].isupper()for j in range(5)))%43)for x in[0,5,10])

PowerShell, 162 bytes

function f{param($f)-join([char[]](65..90)+(0..5))[[convert]::ToInt32(-join($f|%{+($_-cmatch'[A-Z]')}),2)]}
($a=$args[0])+(f $a[4..0])+(f $a[9..5])+(f $a[14..10])

OK, a lot of neat stuff happening in this one. I'll start with the second line.

We take input as a string via $args[0] and set it to $a for use later. This is encapsulated in () so it's executed and the result returned (i.e., $a) so we can immediately string-concatenate it with the results of three function calls (f ...). Each function call passes as an argument the input string indexed in reverse order chunks as a char-array -- meaning, for the example input, $a[4..0] will equal @('0','E','R','0','a') with each entry as a char, not a string.

Now to the function, where the real meat of the program is. We take input as $f, but it's only used way toward the end, so let's focus there, first. Since it's passed as a char-array (thanks to our previous indexing), we can immediately pipe it into a loop with $f|%{...}. Inside the loop, we take each character and perform a case-sensitive regex match with -cmatch which will result in true/false if it's uppercase/otherwise. We cast that as an integer with the encapsulating +(), then that array of 1's and 0's is -joined to form a string. That is then passed as the first parameter in the .NET [convert]::ToInt32() call to change the binary (base 2) into decimal. We use that resultant decimal number to index into a string (-join(...)[...]). The string is first formulated as a range (65..90) that's cast as a char-array, then concatenated with the range (0..5) (i.e., the string is "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345"). All of that is to return the appropriate character from the string.


Jolf, 30 bytes

Finally, a probably still-jolfable! Try it here!

+i mZci5d.p1CρA_Hpu1"[^1]'0"2
    Zci5                      split input into groups of 5
  _m                          map it
        d                      with this function
               _H              reverse H
              A  pu1            and replace in it all uppercase letters with 1
             ρ      "[^1]'0"    replace all non-ones with zeroes
            C               2   parse as binary integer
         .p1                    get the (^)th member of "A...Z0...9"

Python 3, 201 174 138 bytes

Big thanks to Trang Oul for pointing out a function declaration that no longer needed to exist. And Python ternary operators. And some incorrect output. Just...just give him the upvotes.

while c:c-=1;n+=('0','1')[i[c].isupper()]
while l:v=int(n[l-5:l],2);l-=5;i+=(chr(v+65),str(v-26))[v>25]
  • \$\begingroup\$ You use function z() once, you can replace its call and save 25 bytes. Also, your code incorrectly assigns [ instead of 0. \$\endgroup\$
    – Trang Oul
    Commented Jan 12, 2016 at 7:22
  • \$\begingroup\$ Well, that was an embarrassing oversight on my part. Thanks. \$\endgroup\$ Commented Jan 12, 2016 at 14:16
  • 1
    \$\begingroup\$ You can save even more by replacing first if else with this construction and second one with ternary operator. \$\endgroup\$
    – Trang Oul
    Commented Jan 12, 2016 at 14:30

J, 36 bytes



   (,_5(u:@+22+43*<&26)@#.@|.\]~:tolower) 'a0RE000000IJmcN'

Try it online here.


C#, 171 bytes

I'm not really well-practiced in golfing C#, but here's a shot.

s=>{for(var u=s;u.Length>0;u=u.Substring(5)){int p=0,n=u.Substring(0,5).Select(t=>char.IsUpper(t)?1:0).Sum(i=>(int)(i*Math.Pow(2,p++)));s+=(char)(n+65-n/26*17);}return s;}
  • \$\begingroup\$ Suggestions: char.IsUpper(t) can be replaced with t>=65&t<=90 (& on bool in C# is basically a golf-shorter && without short-circuiting). 447 is shorter than 26*17. You don't need to do a separate Select: you can include the ternary directly within the Sum. Consider replacing all those usages of Substring with a loop based on Take instead, e.g. for(int i=0;i<3;i++)s.Skip(i*5).Take(5). For future reference, u!="" would be shorter than u.Length>0 (but that's no longer necessary if you're using Take). \$\endgroup\$
    – Bob
    Commented Jan 11, 2016 at 23:07
  • \$\begingroup\$ The expression n/26*17 is not equivalent to n/442, but other than that, thanks for the suggestions. As stated, I'm not very experienced in golfing in C# so this is all great stuff for me to consider in the future. \$\endgroup\$ Commented Jan 12, 2016 at 14:34
  • \$\begingroup\$ Ah, sorry - I misread that. \$\endgroup\$
    – Bob
    Commented Jan 12, 2016 at 15:06

C# 334

string g(string c){string[]b=new string[]{c.Substring(0,5),c.Substring(5, 5),c.Substring(10)};string o="",w="";for(int i=0,j=0;i<3;i++){char[]t=b[i].ToCharArray();Array.Reverse(t);b[i]=new string(t);o="";for(j=0;j<5;j++){o+=Char.IsUpper(b[i][j])?1:0;}int R=Convert.ToInt32(o,2);char U=R>26?(char)(R+22):(char)(R+65);w+=U;}return c+w;}

If requested, I'll reverse my code back to readable and post it.


Python 3, 87 bytes

lambda s:s+bytes(48+(17+sum((~s[i+j]&32)>>(5-i)for i in range(5)))%43 for j in(0,5,10))

Husk, 26 bytes


Try it online!

Getting the alphabets takes a lot of bytes. Otherwise, I think this qould be at par with the other languages.


05AB1E, 17 bytes


Try it online!


?                 # print the input
 5ô               # split into groups of 5
   ε              # iterate over the groups:
    žK            #   push [a-zA-Z0-9]
      u           #   convert to uppercase
       Ù          #   deduplicate: [A-Z0-9]
        y         #   push the group
         R        #   reverse it
          €       #   for each char:
           .u     #     is it uppercase?
             J    #   join into a binary string
              C   #   convert from binary
               è  #   index into the string
                ? #   print the char at index

Raku, 63 bytes


Try it online!

  • $_ is the single argument to this function, the Salesforce ID.
  • $_ ~ S:g[regex] = expr concatenates $_ with the result of globally replacing the regular expression regex with expr.
  • <:Lu> matches any uppercase letter. (<:Lu>) || . matches an uppercase letter or any other character. An uppercase letter will be stored in a capture group; other characters won't.
  • ((<:Lu>) || .) ** 5 matches five such alternates. $0 is the capture group for the outermost group. Since it is repeated, it will be a list of match objects. Each such match object will contain a capture group match object only if it's an uppercase letter.
  • As for the replacement text, flat('A'..'Z', ^6) evaluates to a list of all uppercase letters followed by the numbers zero through five. The following bracketed expression is an index into that list, providing the replacement character for each set of five ID characters.
  • $0 is a list of match objects, one per character.
  • $0[4…0;0] is a list of the submatch objects, in reverse order. For uppercase letters, this is a match object; for other characters, it's Nil.
  • coerces that list of objects to boolean values. Match objects beome True, and Nil becomes False.
  • :2[...] combines those binary/boolean digits into a number.

K (ngn/k), 41 37 bytes

{x,,/[`c$65+!26;$!6]@2/|+3 5#~"A["'x}

Try it online!

  • ~"A["'x generate a bit mask indicating which values of x (the input) are uppercase letters
  • |+3 5# chunk the input into five-length sections, transposing them and reversing the result to set up for...
  • 2/ converting each column from a list of base-two values to a single base-ten number
  • ,/[`c$65+!26;$!6]@ generate "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345" and index into it with the above
  • x, append the checksum to the input (and implicitly return)

BQN, 40 bytesSBCS


Run online!

Or 31 bytes in dzaima/BQN:


Dyalog APL, 23 bytes

{⍵,⎕A[1+2⊥⍉⎕A∊⍨⌽3 5⍴⍵]}

This is a straightforward mapping of the problem statement.

3 5⍴⍵ ⍝ Reshape the vector into a matrix of
      ⍝ 3 rows by 5 columns.
⌽     ⍝ Reverse the rows.
⎕A∊⍨  ⍝ For each element, check whether they
      ⍝ are a member(∊) of the uppercase
      ⍝ alphabet, ⎕A.
2⊥⍉   ⍝ Decode, ⊥, the columns as a base 2
      ⍝ number. The transpose, ⍉, is so the
      ⍝ rows becomes the columns. The result
      ⍝ is a three element vector.
1+    ⍝ Add one to each element so that they
      ⍝ map to the indexes of the letters on
      ⍝ the alphabet. This is because arrays
      ⍝ in APL are 1 indexed by default. 
⎕A[]  ⍝ Maps the indexes to their
      ⍝ corresponding uppercase letter.
⍵,    ⍝ Finally concatenate the original
      ⍝ vector with the result.
  • 1
    \$\begingroup\$ Welcome to Code Golf! \$\endgroup\$ Commented Oct 5, 2022 at 16:34

Canvas, 31 bytes


Try it here!


Perl 5 -p, 63 bytes


Try it online!


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.