Take the 2-minute tour ×
Programming Puzzles & Code Golf Stack Exchange is a question and answer site for programming puzzle enthusiasts and code golfers. It's 100% free, no registration required.

Write the shortest code that can calculate the check digit for an ISBN-13 from the first 12 digits.

Requirements:

  • Must be a function that accepts a string
  • Returns the full ISBN-13 as a string
  • Uses the correct algorithm to calculate the digit (shown on the Wikipedia page)

Input
978030640615

Output
9780306406157

share|improve this question
1  
Are input and output supposed to contain dashes or only the digits? –  sepp2k Feb 1 '11 at 19:57
    
Updated the description, the input and output are only the digits –  Kevin Brown Feb 1 '11 at 20:06
    
Is outputting the full ISBN-13 acceptable as well? –  GigaWatt Mar 2 '12 at 19:43

30 Answers 30

up vote 13 down vote accepted

Golfscript - 25 chars

{...+(;2%+{+}*3-~10%`+}:f

Whole program version is only 19 chars

...+(;2%+{+}*3-~10%

Check back here for analysis later. Meanwhile check out my old uninspired answer

Golfscript - 32 chars

Similar to the luhn number calculation

{.{2+}%.(;2%{.+}%+{+}*~)10%`+}:f

Analysis for 978030640615

{...}:f this is how you define the function in golfscript
.       store an extra copy of the input string
        '978030640615' '978030640615'
{2+}%   add 2 to each ascii digit, so '0'=>50, I can get away with this instead
        of {15&}% because we are doing mod 10 math on it later
        '978030640615' [59 57 58 50 53 50 56 54 50 56 51 55]
.       duplicate that list
        '978030640615' [59 57 58 50 53 50 56 54 50 56 51 55] [59 57 58 50 53 50 56 54 50 56 51 55]
(;      trim the first element off
        '978030640615' [59 57 58 50 53 50 56 54 50 56 51 55] [57 58 50 53 50 56 54 50 56 51 55]
2%      select every second element
        '978030640615' [59 57 58 50 53 50 56 54 50 56 51 55] [57 50 50 54 56 55]
{.+}%   double each element by adding to itself
        '978030640615' [59 57 58 50 53 50 56 54 50 56 51 55] [114 100 100 108 112 110]
+       join the two lists together
        '978030640615' [59 57 58 50 53 50 56 54 50 56 51 55 114 100 100 108 112 110]
{+}*    add up the items in the list
        '978030640615' 1293
~       bitwise not
        '978030640615' -1294
)       add one
        '978030640615' -1293            
10%     mod 10
        '978030640615' 7
`       convert to str
        '978030640615' '7'
+       join the strings
        '9780306406157'
share|improve this answer
    
After running the code through an interpreter, I think you can save some characters (in your 32 character Luhn-based solution) by getting rid of the first { and the last three characters; }:f. I wonder if the same thing can be done for the first solution... –  Rob Oct 1 '12 at 11:45
    
@MikeDtrick, those characters are how GS defines a function. The 19 char version does what you suggest, but the question asked for a "function" –  gnibbler Oct 2 '12 at 3:45
    
Oh okay, thank you for the explanation. –  Rob Oct 2 '12 at 21:40

Python - 44 chars

f=lambda s:s+`-sum(map(int,s+s[1::2]*2))%10`

Python - 53 chars

def f(s):d=map(int,s);return s+`-sum(d+d[1::2]*2)%10`
share|improve this answer
    
I think f('9780306406159') outputs '97803064061598' instead of '9780306406157' –  Eelvex Feb 2 '11 at 19:31
    
@Eelvex, the input string should always be 12 digits –  gnibbler Feb 2 '11 at 20:47
    
Ah, somehow '9' sneaked in. Sorry... –  Eelvex Feb 2 '11 at 21:19

APL (27 characters)

F←{⍵,⍕10|10-(12⍴1 3)+.×⍎¨⍵}

I'm using Dyalog APL as my interpreter. Here's a quick explanation, mostly from right to left (within the function definition, F←{ ... }):

  • ⍎¨⍵: Execute/evaluate () each (¨) character given in the right argument ().
  • (12⍴1 3): Reshape () the vector 1 3 into a 12-element vector (repeating to fill the gaps).
  • +.×: Take the dot product (+.×) of its left argument ((12⍴1 3)) and its right argument (⍎¨⍵).
  • 10-: Subtract from 10.
  • 10|: Find the remainder after division by 10.
  • : Format the number (i.e., give a character representation).
  • ⍵,: Append (,) our calculated digit to the right argument.
share|improve this answer
    
Thanks for the explanation! –  Felix Dombek Oct 2 '12 at 1:34

Haskell - 54 characters

i s=s++show(sum[-read[c]*m|c<-s|m<-cycle[1,3]]`mod`10)

This requires support for parallel list comprehensions, which is supported by GHC (with the -XParallelListComp flag) and Hugs (with the -98 flag).

share|improve this answer

PHP - 86 85 82 chars

function c($i){for($a=$s=0;$a<12;)$s+=$i[$a]*($a++%2?3:1);return$i.(10-$s%10)%10;}

Re-format and explanation:

function c($i){                     // function c, $i is the input

    for($a=$s=0;$a<12;)             // for loop x12 - both $a and $s equal 0
                                    // notice there is no incrementation and
                                    // no curly braces as there is just one
                                    // command to loop through

        $s+=$i[$a]*($a++%2?3:1);    // $s (sum) is being incremented by
                                    // $ath character of $i (auto-casted to
                                    // int) multiplied by 3 or 1, depending
                                    // wheter $a is even or not (%2 results
                                    // either 1 or 0, but 0 == FALSE)
                                    // $a is incremented here, using the
                                    // post-incrementation - which means that
                                    // it is incremented, but AFTER the value
                                    // is returned

    return$i.(10-$s%10)%10;         // returns $i with the check digit
                                    // attached - first it is %'d by 10,
                                    // then the result is subtracted from
                                    // 10 and finally %'d by 10 again (which
                                    // effectively just replaces 10 with 0)
                                    // % has higher priority than -, so there
                                    // are no parentheses around $s%10
}
share|improve this answer
    
Exactly the approach I took in my C# answer. Seems PHP is ~9 characters more efficient! –  Nellius Feb 2 '11 at 0:15

Haskell, 78 71 66 characters

i s=s++(show$mod(2-sum(zipWith(*)(cycle[1,3])(map fromEnum s)))10)
share|improve this answer

Ruby - 73 65 chars

f=->s{s+((2-(s+s.gsub(/.(.)/,'\1')*2).bytes.inject(:+))%10).to_s}
share|improve this answer
    
"\\1" -> '\1'? –  Nemo157 Feb 1 '11 at 22:42
    
@Nemo, thanks, my ruby is a bit rusty –  gnibbler Feb 1 '11 at 23:24
    
Use Ruby 1.9 syntax f=->s{...}. Save 6 chars. Also write s<<(...).to_sinstead of adding 48 and use Fixnum#chr. –  Łukasz Niemier Mar 10 '12 at 16:58

C# (94 characters)

string I(string i){int s=0,j=0;for(;j<12;)s+=(i[j]-48)*(j++%2<1?1:3);return i+((10-s%10)%10);}

With linebreaks/whitespace for readability:

string I(string i) 
{ 
    int s = 0, j = 0;
    for (; j < 12; )
        s += (i[j] - 48) * (j++ % 2 < 1 ? 1 : 3); 
    return i + ((10 - s % 10) % 10); 
}

Tested on several ISBNs from books on my shelf, so I know it's working!

share|improve this answer

Perl, 53 chars

sub i{$_=shift;s/(.)(.)/$s+=$1+$2*3/ge;$_.(10-$s)%10}
share|improve this answer

Windows PowerShell, 57

filter i{$_+(990-($_-replace'(.)(.)','+$1+3*$2'|iex))%10}
share|improve this answer
    
Side note to the person suggesting an edit: Comment the next time please because I golf better than you ;-) –  Joey Jun 1 '13 at 21:54

Ruby - 80 characters

def f s;s+(10-s.bytes.zip([1,3]*6).map{|j,k|(j-48)*k}.inject(:+)%10).to_s[0];end
share|improve this answer

Python - 91, 89

0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
|         |         |         |         |         |         |         |         |         |
 def c(i):return i+`(10-(sum(int(x)*3for x in i[1::2])+sum(int(x)for x in i[::2]))%10)%10`
share|improve this answer
    
Spaces are optional between the first argument and for (and in and the third) in a list comprehension so long as it can be split up by the parser (not using a variable name). -2 chars there. –  Nick T Feb 3 '11 at 3:03
    
@Nick T, fixed. Thanks. –  grokus Feb 3 '11 at 3:06

J - 55 45

f=:3 :'y,":10|10-10|+/(12$1 3)*(i.12)(".@{)y'

eg

f '978030640615'
9780306406157

old way:

f=:,":@(10(10&|@-)(10&|@+/@((12$1 3)*(i.12)&(".@{))))
share|improve this answer

dc, 44 chars

[d0r[I~3*rI~rsn++lndZ0<x]dsxx+I%Ir-I%rI*+]sI

Invoke as lIx, e.g:

dc -e'[d0r[I~3*rI~rsn++lndZ0<x]dsxx+I%Ir-I%rI*+]sI' -e '978030640615lIxp'
share|improve this answer

Q, 36 chars

{x,-3!10-mod[;10]sum(12#1 3)*"I"$'x}
share|improve this answer

C# – 89 77 characters

string I(string s){return s+(9992-s.Sum(x=>x-0)-2*s.Where((x,i)=>i%2>0).Sum(x=>x-0))%10;}

Formatted for readability:

string I(string s)
{
    return s +
            (9992
            - s.Sum(x => x - 0)
            - 2 * s.Where((x, i) => i%2 > 0).Sum(x => x - 0)
            ) % 10;
}

We do not multiply by one or three, we just add everything, plus we add all even-placed characters one more time, multiplied by two.

9992 is large enough so that the sum of all ASCII characters is less than that (so that we may mod by 10 and be sure the result is positive, no need to mod by 10 twice), and is not divisible by zero because we add up all those extra 2*12*48 (twelve ASCII digits, weighed by 1 and 3) == 1152, which allows us to spare one extra character (instead of twice subtracting 48, we subtract 0 just to convert from char to int, but instead of 990, we need to write 9992).

But then again, even though much less beautiful ;-), this old-school solution gets us to 80 characters (but this is almost C-compatible):

string T(string i){int s=2,j=0;for(;j<12;)s+=i[j]*(9-j++%2*2);return i+s%10;}
share|improve this answer

D - 97 characters

auto f(string s){int n;foreach(i,c;s)n+=((i&1)*2+1)*(c-48);return s~cast(char)((10-n%10)%10+48);}

Formatted more legibly:

auto f(string s)
{
    int n;

    foreach(i, c; s)
        n += ((i & 1) * 2 + 1) * (c - 48);

    return s ~ cast(char)((10 - n % 10) % 10 + 48);
}

The verbosity of D's cast operator definitely makes it harder to write obsessively short code though.

share|improve this answer

Java - 161 Characters :(

int b[]=new int[a.length];
int d=0,n=0,j=1;
for(char c:a.toCharArray())b[d++]=Integer.valueOf(c+"");
for(int i:b)n+=(j++%2==0)?(i*3):(i*1);
return a+(10-(n%10));
share|improve this answer
    
This answer does not satisfy the first requirement, since it is not a function. –  han Oct 3 '12 at 17:49

Q (44 chars)

f:{x,string 10-mod[;10]0+/sum@'2 cut"I"$/:x}
share|improve this answer
    
This is actually incorrect I think –  slackwear Mar 9 '12 at 19:53

Scala 84

def b(i:String)=i+(10-((i.sliding(2,2).map(_.toInt).map(k=>k/10+k%10*3).sum)%10)%10)

Testing:

val isbn="978030640615"
b(isbn)

Result:

"9780306406157"
share|improve this answer

C, 80 79 characters

The function modifies the string in place, but returns the original string pointer to satisfy the problem requirements.

s;char*f(char*p){for(s=2;*p;s+=7**p++)s+=9**p++;*p++=48+s%10;*p=0;return p-13;}

Some explanation: Instead of subtracting 48 (the ASCII value of the digit 0) from each input character, the accumulator s is initialized so that it is modulo 10 equal to 48+3*48+48+3*48...+48+3*48 = 24*48 = 1152. The step 10-sum can be avoided by accumulating s by subtraction instead of addition. However, the module operator % in C would not give a usable result if s was negative, so instead of using s-= the multipliers 3 and 1 are replaced by -3=7 modulo 10 and -1=9 modulo 10, respectively.

Test harness:

#include <stdio.h>
#define N 12
int main()
{
     char b[N+2];
     fgets(b, N+1, stdin);
     puts(f(b));
     return 0;
}
share|improve this answer

Groovy 75, 66 chars

i={int i;it+(10-it.inject(0){t,c->t+(i++&1?:3)*(c as int)}%10)%10}

use:

String z = "978030640615"
println i(z)

-> 9780306406157
share|improve this answer

MATLAB - 82 chars

function c(i)
[i num2str(mod(10-mod(sum(str2num(i(:)).*repmat([1;3],6,1)),10),10))]
share|improve this answer

R, 147 characters

f=function(v){s=as.numeric(strsplit(v,"")[[1]]);t=0;for(i in 1:12)if(i%%2==0)t=t+s[i]*3 else t=t+s[i];paste(v,(10-(t%%10))%%10,collapse="",sep="")}

Usage:

f("978030640615")
[1] "9780306406157"
share|improve this answer

J, 25

,[:":10|0(-+/)"."0*1 3$~#
   f =: ,[:":10|0(-+/)"."0*1 3$~#
   f '978030640615'
9780306406157
share|improve this answer

APL (25)

{⍵,⍕10-10|+/(⍎¨⍵)×12⍴1,3}
share|improve this answer

Javascript: 73 71 chars

function _(b){for(i=s=0;12>i;)s+=((i*2&2)+1)*b[i++];return b+(10-s%10)}

Usage:

_('978030640615') //"9780306406157"
share|improve this answer

Python

>>>r=input()
>>>a=0
>>>for x in r[::2]:
    a+=int(x)
>>>for x in r[1::2]:
    a+=int(x)*3
>>>a=(10-(a%10))%10
print(r+str(a))
share|improve this answer
    
I've added the language name to your answer; I was going to add a character count too but it looks like you've cut and paste from the REPL. Could you change this so it's a function as requested in the question and add a character count? –  Gareth Oct 29 '12 at 12:53

Clojure 264 -> 241 characters:

(defn s [a b] (let [c (count a)] (if (= 0 c) b (s (rest a) (+ b (* (if (= 0 (m c 2)) 1 3) (first a)))))))
(defn m [a b] (let [f (mod a b)] (if (= f 10) 0 f)))
(defn c [a] (str a (m (- 10 (m (s (map #(read-string %) (map str a)) 0) 10)) 10)))

Usage

(c "978030640615")

Non minified:

(defn sum [collection total]
  (let [len (count collection)]
    (if (= 0 len)
         total
         (sum (rest collection)
           (+ total
            (* (if (= 0 (mod len 2)) 1 3)
             (first collection)))))))

(defn mods [a b]
  (let [f (mod a b)]
    (if (= f 10) 
      0
      f)))


(defn create [isbn] 
  (str isbn
       (mods (- 10 (mods (sum (map #(read-string %) (map str isbn)) 0) 10)) 10))) 

Usage:

(create "978030640615")

New to Clojure so I have to believe that can be knocked down.

share|improve this answer

Tcl, 79

proc f c {lmap a\ b [split $c {}] {incr d [expr $a+3*$b]};list $c[expr -$d%10]}
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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