30
\$\begingroup\$

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.

\$\endgroup\$
  • 1
    \$\begingroup\$ Are input and output supposed to contain dashes or only the digits? \$\endgroup\$ – sepp2k Feb 1 '11 at 19:57
  • 1
    \$\begingroup\$ Updated the description, the input and output are only the digits \$\endgroup\$ – Kevin Brown Feb 1 '11 at 20:06
  • \$\begingroup\$ Is outputting the full ISBN-13 acceptable as well? \$\endgroup\$ – Mr. Llama Mar 2 '12 at 19:43
  • 6
    \$\begingroup\$ Note that questions should really be self contained, so it would be helpful to include a description of the algorithm here. \$\endgroup\$ – FlipTack Nov 19 '17 at 10:01
  • \$\begingroup\$ 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... \$\endgroup\$ – user58988 Mar 9 '19 at 19:34

47 Answers 47

1
2
1
\$\begingroup\$

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.

| improve this answer | |
\$\endgroup\$
1
\$\begingroup\$

05AB1E, 11 bytes

SιO`3*+θTα«

Try it online or verify a few more test cases.

Explanation:

S            # Convert the (implicit) input to a list of digits
             #  i.e. 978030640615 → [9,7,8,0,3,0,6,4,0,6,1,5]
 ι           # Uninterleave it
             #  → [[9,8,3,6,0,1],[7,0,0,4,6,5]]
  O          # Sum each inner list
             #  → [27,22]
   `         # Push them both separated to the stack
    3*       # Multiply the top one by 3
             #  → 66
      +      # Sum them together
             #  → 93
       θ     # Only leave the last digit
             #  → 3
        Tα   # Take the absolute difference of this digit with 10
             #  → 7
          «  # And append this digit to the (implicit) input
             #  → 9780306406157
             # (after which it is output implicitly as result)
| improve this answer | |
\$\endgroup\$
0
\$\begingroup\$

MATLAB - 82 chars

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

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"
| improve this answer | |
\$\endgroup\$
0
\$\begingroup\$

J, 25

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

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"
| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Why not define _ as an arrow function too? That saves quite a few characters :) \$\endgroup\$ – Max Mar 9 '19 at 21:20
0
\$\begingroup\$

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.

| improve this answer | |
\$\endgroup\$
0
\$\begingroup\$

Tcl, 79

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

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!

| improve this answer | |
\$\endgroup\$
0
\$\begingroup\$

Tcl, 64 bytes

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

Try it online!

| improve this answer | |
\$\endgroup\$
0
\$\begingroup\$

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
| improve this answer | |
\$\endgroup\$
0
\$\begingroup\$

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
| improve this answer | |
\$\endgroup\$
0
\$\begingroup\$

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.

| improve this answer | |
\$\endgroup\$
0
\$\begingroup\$

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

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ I don't think you need .absoluteValue as all digits are non-negative, So this works for 69 bytes. \$\endgroup\$ – ovs Mar 5 '19 at 16:15
  • \$\begingroup\$ you're right! thanks! I removed it after, to get it to 77 bytes, but your edit was much better \$\endgroup\$ – Adam Mar 5 '19 at 16:21
0
\$\begingroup\$

><>, 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
| improve this answer | |
\$\endgroup\$
0
\$\begingroup\$

Perl 5, 48 bytes

sub f{"@_"=~s/(.)(.)/$t-=$1+3*$2/ger;"@_".$t%10}

Try it online!

Doing this as a full program would be shorter:

Perl 5 -p, 31 bytes

s/(.)(.)/$\-=$1+3*$2/ger;$\%=10

Try it online!

| improve this answer | |
\$\endgroup\$
-1
\$\begingroup\$

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))
| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ 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? \$\endgroup\$ – Gareth Oct 29 '12 at 12:53
1
2

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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