28
\$\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\$ – RosLuP Mar 9 at 19:34

45 Answers 45

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

\$\endgroup\$
0
\$\begingroup\$

Tcl, 79

proc f c {lmap a\ b [split $c {}] {incr d [expr $a+3*$b]};list $c[expr -$d%10]}
\$\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!

\$\endgroup\$
0
\$\begingroup\$

Tcl, 64 bytes

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

Try it online!

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

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
\$\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
\$\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.

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

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

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

\$\endgroup\$
0
\$\begingroup\$

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
\$\endgroup\$
0
\$\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.

\$\endgroup\$

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.