Calculate ISBN-13 check digit

28

6

Write a function which, given the first 12 digits of an ISBN-13 code, will calculate the entire ISBN via calculating and appending an appropriate check digit.

Your function's input is a string containing the first 12 digits of the ISBN. Its output is a string containing all 13 digits.

Formal specification

Write a function which, when given a string s consisting entirely of exactly 12 decimal digits (and no other characters), returns a string t with the following properties:

  • t consists of exactly 13 decimal digits (and no other characters);
  • s is a prefix of t;
  • the sum of all digits in odd positions in t (i.e. the first, third, fifth, etc.), plus three times the sum of all digits in even positions in t (i.e. the second, fourth, sixth, etc), is a multiple of 10.

Example / test case

Input
978030640615

Output
9780306406157

Victory condition

As a challenge, the shortest answer wins.

Kevin Brown

Posted 2011-02-01T18:58:11.477

Reputation: 5 756

Is outputting the full ISBN-13 acceptable as well? – Mr. Llama – 2012-03-02T19:43:20.793

6Note that questions should really be self contained, so it would be helpful to include a description of the algorithm here. – FlipTack – 2017-11-19T10:01:45.123

For me above post is poor of example test... There would be at last 10 isbn and one of that has to return 0 as last digit... – RosLuP – 2019-03-09T19:34:05.890

1Are input and output supposed to contain dashes or only the digits? – sepp2k – 2011-02-01T19:57:08.123

1Updated the description, the input and output are only the digits – Kevin Brown – 2011-02-01T20:06:38.837

Answers

14

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'

gnibbler

Posted 2011-02-01T18:58:11.477

Reputation: 14 170

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 – 2012-10-01T11:45:57.670

@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 – 2012-10-02T03:45:30.013

Oh okay, thank you for the explanation. – Rob – 2012-10-02T21:40:53.823

You don't need the :f (yeah, I know functions were commonly named back then). – Erik the Outgolfer – 2018-10-22T17:23:34.930

8

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`

gnibbler

Posted 2011-02-01T18:58:11.477

Reputation: 14 170

I think f('9780306406159') outputs '97803064061598' instead of '9780306406157' – Eelvex – 2011-02-02T19:31:05.200

@Eelvex, the input string should always be 12 digits – gnibbler – 2011-02-02T20:47:31.203

Ah, somehow '9' sneaked in. Sorry... – Eelvex – 2011-02-02T21:19:04.187

7

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).

Joey Adams

Posted 2011-02-01T18:58:11.477

Reputation: 9 929

Do you not need to include that flag in the count? Besides that, you can replace [1,3] by [9,7] and remove the - which saves you a byte :) – ბიმო – 2017-11-19T22:02:12.963

7

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.

Dillon Cower

Posted 2011-02-01T18:58:11.477

Reputation: 2 192

6

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
}

Aurel Bílý

Posted 2011-02-01T18:58:11.477

Reputation: 1 083

Exactly the approach I took in my C# answer. Seems PHP is ~9 characters more efficient! – Nellius – 2011-02-02T00:15:23.307

6

Windows PowerShell, 57

filter i{$_+(990-($_-replace'(.)(.)','+$1+3*$2'|iex))%10}

Joey

Posted 2011-02-01T18:58:11.477

Reputation: 12 260

5

Haskell, 78 71 66 characters

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

sepp2k

Posted 2011-02-01T18:58:11.477

Reputation: 1 679

5

Ruby - 73 65 chars

f=->s{s+((2-(s+s.gsub(/.(.)/,'\1')*2).bytes.inject(:+))%10).to_s}

gnibbler

Posted 2011-02-01T18:58:11.477

Reputation: 14 170

Use Ruby 1.9 syntax f=->s{...}. Save 6 chars. Also write s<<(...).to_sinstead of adding 48 and use Fixnum#chr. – Hauleth – 2012-03-10T16:58:58.793

"\\1" -> '\1'? – Nemo157 – 2011-02-01T22:42:12.157

@Nemo, thanks, my ruby is a bit rusty – gnibbler – 2011-02-01T23:24:13.777

4

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`

grokus

Posted 2011-02-01T18:58:11.477

Reputation: 1 043

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 – 2011-02-03T03:03:50.083

4

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!

Nellius

Posted 2011-02-01T18:58:11.477

Reputation: 568

4

J - 55 45 38

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

eg

f '978030640615'
9780306406157

old way:

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

Eelvex

Posted 2011-02-01T18:58:11.477

Reputation: 5 204

1(i.12)(".@{)y can be replaced with "."0 y – J Guy – 2018-10-23T06:36:59.330

4

Perl, 53 chars

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

ninjalj

Posted 2011-02-01T18:58:11.477

Reputation: 3 018

4

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

Mormegil

Posted 2011-02-01T18:58:11.477

Reputation: 1 148

3

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

Nemo157

Posted 2011-02-01T18:58:11.477

Reputation: 1 891

3

Q, 36 chars

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

tmartin

Posted 2011-02-01T18:58:11.477

Reputation: 3 917

3

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'

ninjalj

Posted 2011-02-01T18:58:11.477

Reputation: 3 018

2

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.

Jonathan M Davis

Posted 2011-02-01T18:58:11.477

Reputation: 705

2

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));

Octavian A. Damiean

Posted 2011-02-01T18:58:11.477

Reputation: 127

This answer does not satisfy the first requirement, since it is not a function. – han – 2012-10-03T17:49:20.343

1

Perl 6, 29 bytes

{$_~-:1[.comb «*»(1,3)]%10}

Try it online!

nwellnhof

Posted 2011-02-01T18:58:11.477

Reputation: 10 037

So base 1 can be used as a substitute for sum? Interesting! – Jo King – 2018-10-23T06:22:24.237

2@JoKing It's actually a very old trick in the world of APL and J :) – Bubbler – 2018-10-23T06:31:32.723

Wrong result for 978186197371 it seems to be 8 not 9... – RosLuP – 2019-03-09T17:11:35.993

Excuse me I think I had pasted the wrong number – RosLuP – 2019-03-09T17:20:00.810

1

Python 2, 78 76 bytes

lambda n:n+`10-(sum(int(a)+3*int(b)for a,b in zip(n[::2],n[1::2]))%10or 10)`

Try it online!

Takes a string as an argument.

Explanation:

Using python slice notation, converts a string into a list of character pairs. ("978030640615" -> [("9","7"), ("8", "0"), ("3", "0"), ("6", "4"), ("0", "6"), ("1", "5")] )

For that list of pairs, converts each item into an integer and returns a+3b.

Sums all the results.

Gets the sum modulo 10, OR 10 if the remainder is 0. (This prevents the final digit from being 10 instead of 0.)

Removes the remainder from 10 to get the check digit.

Converts the calculated check digit to a string via deprecated backtick expression.

Returns the original number plus the calculated check digit.

Edit:

Saved 2 byes by removing spaces (thanks Jo King!).

Triggernometry

Posted 2011-02-01T18:58:11.477

Reputation: 765

You can remove the spaces before for and or – Jo King – 2018-10-23T06:19:11.300

Result to me that 978186197371 has last digit 8 and not 9... I get that number from the only link I print in my Apl solution – RosLuP – 2019-03-09T17:09:38.367

Excuse me I think I have pasted the wrong number... – RosLuP – 2019-03-09T17:18:45.273

1

APL (Dyalog Unicode), 18 bytesSBCS

Anonymous tacit prefix function taking string as argument. Using Bubbler's approach.

⊢,∘⍕10|⍎¨+.×9 7⍴⍨≢

Try it online!

 length of argument (12)

9 7⍴⍨ cyclically reshape [9,7] to that length

+.× dot product of the following with that:

⍎¨ `evaluate each character

10| mod-10 of that

,∘⍕ prepend the following the the stringification of that:

 the unmodified argument

Adám

Posted 2011-02-01T18:58:11.477

Reputation: 37 779

1

dc, 25 bytes

dn[A~9z^8+*rd0<M+]dsMxA%p

Try it online!

I know there's already a dc answer here, but 25<44 so I guess I feel 19 bytes of okay about it. This uses the fact that 8+9^z is equivalent to either -3 or -1 mod 10 depending on whether z is even or odd. So I use A~ to break the number into digits on the stack, but as I build the stack I multiply each digit by 8+9^z where z is the current stack size. Then I add them all as the function stack unrolls, and print the last digit.

Sophia Lechner

Posted 2011-02-01T18:58:11.477

Reputation: 1 200

1

Q (44 chars)

f:{x,string 10-mod[;10]0+/sum@'2 cut"I"$/:x}

skeevey

Posted 2011-02-01T18:58:11.477

Reputation: 4 139

This is actually incorrect I think – skeevey – 2012-03-09T19:53:08.710

1

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"

user unknown

Posted 2011-02-01T18:58:11.477

Reputation: 4 210

1

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

han

Posted 2011-02-01T18:58:11.477

Reputation: 1 226

1

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

Armand

Posted 2011-02-01T18:58:11.477

Reputation: 499

1

APL (25)

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

marinus

Posted 2011-02-01T18:58:11.477

Reputation: 30 224

The algo has to end with 10| because else it could return 10 in place of 0 – RosLuP – 2019-03-09T19:31:33.607

0

Tcl, 79

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

Johannes Kuhn

Posted 2011-02-01T18:58:11.477

Reputation: 7 122

0

Tcl, 87 bytes

proc C s {lmap c [split $s ""] {incr t [expr [incr i]%2?$c:3*$c]}
list $s[expr -$t%10]}

Try it online!

sergiol

Posted 2011-02-01T18:58:11.477

Reputation: 3 055

0

Tcl, 64 bytes

proc C s {list $s[expr ([regsub -all (.)(.) $s {-\1-3*\2}])%10]}

Try it online!

sergiol

Posted 2011-02-01T18:58:11.477

Reputation: 3 055

0

Husk, 16 bytes

S:(→sΣz¤*or;¢"97

Try it online!

Explanation

Instead of subtracting the sum from 10 I use negated coefficients, the rest is pretty self-explanatory:

S:(              )  -- apply the following function and append result to itself:
   →s               -- mod 10 / last digit
     Σ              -- sum
      z      ¢"97   -- zip input with ['9','7','9','7'...] under:
       ¤ (r;)       --   convert both chars to number, then
        *           --   multiply

ბიმო

Posted 2011-02-01T18:58:11.477

Reputation: 15 345

0

Stax, 11 10 bytes

╣Γ♠☼ù≤m╒X₧

Run and debug it

This is a complete program instead of a function, as stax does not implement functions.

Unpacked, ungolfed, and commented, it looks like this.

q   print input without popping and without newline
E   explode into array of decimal digits
97E [9,7] (this will become the repetition counts)
:B  repeat elements; repetition counts wrap around to cover all input digits
|+  get sum of array with repeated digts
A%  modulus 10
P   print

Run this one

recursive

Posted 2011-02-01T18:58:11.477

Reputation: 8 616

0

J, 21 bytes

,10":@|1#."."+*9 7$~#

Try it online!

How it works

,10":@|1#."."+*9 7$~#
                    #  Take length of input
               9 7$~   Repeat 9 7 that many times
          "."+*        Multiply (elementwise) to the digits
       1#.             Sum
 10   |                Modulo 10
   ":@                 Format to string
,                      Append to the end of the input string

The negative of a + 3b + c + 3d + ... modulo 10 is equal to 9a + 7b + 9c + 7d + ... modulo 10.

Bubbler

Posted 2011-02-01T18:58:11.477

Reputation: 16 616

0

Powershell, 55 53 52 bytes

Inspired by Joey's answer. I'm impressed with her regex.

"$args"+($args-replace'(.)(.)','+40-$1-3*$2'|iex)%10

Powershell, 55 bytes, whithout regex

param($s)$s[1,3,5,7,9,11*2+0..11]|%{$r+=58-$_};$s+$r%10

Explanation:

  • the index range = (the even positions 1,3,5,7,9,11 repeated 2 times + all positions 0..11)
  • $r+=58-$_ makes a sum of supplements up to 10+[char]'0'
  • $s+$r%10 outputs the result

Test script:

$f = {

param($s)$s[1,3,5,7,9,11*2+0..11]|%{$r+=58-$_};$s+$r%10

}

@(
    ,("978030640615" , "9780306406157")

) | % {
    $s,$expected = $_
    $result = &$f $s
    "$($result-eq$expected)"
    $s
    $result
}

Output:

True
978030640615
9780306406157

mazzy

Posted 2011-02-01T18:58:11.477

Reputation: 4 832

0

MBASIC, 106 bytes

1 INPUT T$:FOR I=1 TO 12 STEP 2:O=O+VAL(MID$(T$,I,1)):E=E+3*VAL(MID$(T$,I+1,1)):NEXT:PRINT 10-(O+E) MOD 10

It ain't pretty, but it works.

? 978030640615
7

wooshinyobject

Posted 2011-02-01T18:58:11.477

Reputation: 171

0

APL(NARS), chars 38, bytes 76

{⍵,10∣10-10∣+/w,2×w[v/⍨∼2∣v←⍳≢w←⍎¨⍕⍵]}

I had some other test for see better and get definition from Wikipedia, and examples in the page: https://isbn-information.com/check-digit-for-the-13-digit-isbn.html test:

  h←{⍵,10∣10-10∣+/w,2×w[v/⍨∼2∣v←⍳≢w←⍎¨⍕⍵]}
  h 978186197271
978186197271 2 
  h 978168197271
978168197271 8 
  h 978186197371      
978186197371 9 
  h 978186197375      
978186197375 7 

the formula i use would be in one not APL language if w is the argument =>

f(w)==w,(10-(sum(digit(w),2*digitsEVENINDICES(w))mod 10))mod 10

comment:

{⍵,10∣10-10∣+/w,2×w[v/⍨∼2∣v←⍳≢w←⍎¨⍕⍵]}
                   v/⍨∼2∣v←⍳≢w←⍎¨⍕⍵] call w the array of digits of ⍵, return even index
               2×w[                  consider all the digit even of w and multipy for 2
             w,                      put above that array, the array w
           +/                        sum all
      10-10∣                          10 - (above sum)mod 10
    10∣                               above mod 10
 {⍵,                                 add as digit to the number of argument, and return all that

RosLuP

Posted 2011-02-01T18:58:11.477

Reputation: 3 036

0

Kotlin, 120 108 83 77 69 bytes

{s->s+(10-(s.map{it.toInt()-48}.chunked(2).sumBy{it[0]+3*it[1]})%10)}

Try it online!


Thanks to @ovs for removing 8 bytes!!

Adam

Posted 2011-02-01T18:58:11.477

Reputation: 101

I don't think you need .absoluteValue as all digits are non-negative, So this works for 69 bytes.

– ovs – 2019-03-05T16:15:03.390

you're right! thanks! I removed it after, to get it to 77 bytes, but your edit was much better – Adam – 2019-03-05T16:21:53.530

0

><>, 24 bytes

0{:9)68**-:nl2%2*7+*+a%!

Try it online!

Explanation

0                      !  # initiates sum (final digit) as 0
 {                        # get the next digit
  :9)68**-                # convert from char to int for any iteration except the last
          :n              # print the digit
            l2%2*7+*      # multiply the digit by either 9 or 7 alternately
                    +     # add to sum
                     a%   # mod sum by 10

Emigna

Posted 2011-02-01T18:58:11.477

Reputation: 50 798

0

JavaScript, 57 bytes

f=s=>s+(10-s.split``.reduce((a,b,i)=>a+b*(3-i%2*2),0)%10)

You can invoke it like this:

f("978030640615")

Effectively, it goes through each (digit, index) pair (b, i), and works out i % 2 (is i odd?), multiplies it by 2, and subtracts it from 3. Take a look at this:

For odd numbers:

  • (i % 2) is 1
  • ((i % 2) * 2) is 2
  • 3 - ((i % 2) * 2) is 1

For even numbers:

  • (i % 2) is 0
  • ((i % 2) * 2) is 0
  • 3 - ((i % 2) * 2) is 3

And so we have now generated the correct multipliers. From there it is simple, just add the current accumulator value, initialised at 0, to the current character multiplied by this generated multiplier. JavaScript automatically casts string to numbers when you do this multiplication.

Calculating the last digit is done by taking the last digit (by computing the value modulo 10), and subtracting this from 10.

Max

Posted 2011-02-01T18:58:11.477

Reputation: 131

0

MATLAB - 82 chars

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

Griffin

Posted 2011-02-01T18:58:11.477

Reputation: 4 349

0

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"

Paolo

Posted 2011-02-01T18:58:11.477

Reputation: 696

0

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))

Ashwini Chaudhary

Posted 2011-02-01T18:58:11.477

Reputation: 169

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 – 2012-10-29T12:53:39.180

0

J, 25

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

ephemient

Posted 2011-02-01T18:58:11.477

Reputation: 1 601

0

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"

Paul

Posted 2011-02-01T18:58:11.477

Reputation: 256

Why not define _ as an arrow function too? That saves quite a few characters :) – Max – 2019-03-09T21:20:23.863

0

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.

Programmin Tool

Posted 2011-02-01T18:58:11.477

Reputation: 111