Skip to content

Commit 2fe425a

Browse files
authored
Add new practice exercise: isbn-verifier (#109)
1 parent 245971e commit 2fe425a

File tree

10 files changed

+277
-0
lines changed

10 files changed

+277
-0
lines changed

config.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,14 @@
250250
"prerequisites": [],
251251
"difficulty": 8
252252
},
253+
{
254+
"slug": "isbn-verifier",
255+
"name": "ISBN Verifier",
256+
"uuid": "a0a1cb68-b587-4d30-bf1c-819814847637",
257+
"practices": [],
258+
"prerequisites": [],
259+
"difficulty": 8
260+
},
253261
{
254262
"slug": "isogram",
255263
"name": "Isogram",
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# Instructions
2+
3+
The [ISBN-10 verification process][isbn-verification] is used to validate book identification numbers.
4+
These normally contain dashes and look like: `3-598-21508-8`
5+
6+
## ISBN
7+
8+
The ISBN-10 format is 9 digits (0 to 9) plus one check character (either a digit or an X only).
9+
In the case the check character is an X, this represents the value '10'.
10+
These may be communicated with or without hyphens, and can be checked for their validity by the following formula:
11+
12+
```text
13+
(d₁ * 10 + d₂ * 9 + d₃ * 8 + d₄ * 7 + d₅ * 6 + d₆ * 5 + d₇ * 4 + d₈ * 3 + d₉ * 2 + d₁₀ * 1) mod 11 == 0
14+
```
15+
16+
If the result is 0, then it is a valid ISBN-10, otherwise it is invalid.
17+
18+
## Example
19+
20+
Let's take the ISBN-10 `3-598-21508-8`.
21+
We plug it in to the formula, and get:
22+
23+
```text
24+
(3 * 10 + 5 * 9 + 9 * 8 + 8 * 7 + 2 * 6 + 1 * 5 + 5 * 4 + 0 * 3 + 8 * 2 + 8 * 1) mod 11 == 0
25+
```
26+
27+
Since the result is 0, this proves that our ISBN is valid.
28+
29+
## Task
30+
31+
Given a string the program should check if the provided string is a valid ISBN-10.
32+
Putting this into place requires some thinking about preprocessing/parsing of the string prior to calculating the check digit for the ISBN.
33+
34+
The program should be able to verify ISBN-10 both with and without separating dashes.
35+
36+
## Caveats
37+
38+
Converting from strings to numbers can be tricky in certain languages.
39+
Now, it's even trickier since the check digit of an ISBN-10 may be 'X' (representing '10').
40+
For instance `3-598-21507-X` is a valid ISBN-10.
41+
42+
[isbn-verification]: https://en.wikipedia.org/wiki/International_Standard_Book_Number
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"authors": [
3+
"jimmytty"
4+
],
5+
"files": {
6+
"solution": [
7+
"isbn-verifier.sql"
8+
],
9+
"test": [
10+
"isbn-verifier_test.sql"
11+
],
12+
"example": [
13+
".meta/example.sql"
14+
]
15+
},
16+
"blurb": "Check if a given string is a valid ISBN-10 number.",
17+
"source": "Converting a string into a number and some basic processing utilizing a relatable real world example.",
18+
"source_url": "https://en.wikipedia.org/wiki/International_Standard_Book_Number#ISBN-10_check_digit_calculation"
19+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
UPDATE "isbn-verifier"
2+
SET result = FALSE
3+
WHERE GLOB(
4+
'[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9X]',
5+
REPLACE(isbn, '-', '')
6+
) = 0
7+
;
8+
9+
UPDATE "isbn-verifier"
10+
SET result = (
11+
WITH RECURSIVE verifier(isbn, digit, pos) AS (
12+
VALUES((
13+
WITH RECURSIVE cleaner(string, char) AS (
14+
VALUES(isbn, '')
15+
UNION ALL
16+
SELECT SUBSTRING(string, 2), SUBSTRING(string, 1, 1)
17+
FROM cleaner
18+
WHERE string <> ''
19+
)
20+
SELECT GROUP_CONCAT(char, '')
21+
FROM cleaner WHERE GLOB('[0-9X]', char)
22+
), '', 11)
23+
UNION ALL
24+
SELECT
25+
SUBSTRING(isbn, 2),
26+
IIF(SUBSTRING(isbn, 1, 1) = 'X', 10, SUBSTRING(isbn, 1, 1)),
27+
pos - 1
28+
FROM verifier
29+
WHERE isbn <> ''
30+
) SELECT SUM(digit * pos) % 11 = 0 FROM verifier WHERE digit <> ''
31+
)
32+
WHERE result ISNULL
33+
;
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# This is an auto-generated file.
2+
#
3+
# Regenerating this file via `configlet sync` will:
4+
# - Recreate every `description` key/value pair
5+
# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications
6+
# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion)
7+
# - Preserve any other key/value pair
8+
#
9+
# As user-added comments (using the # character) will be removed when this file
10+
# is regenerated, comments can be added via a `comment` key.
11+
12+
[0caa3eac-d2e3-4c29-8df8-b188bc8c9292]
13+
description = "valid isbn"
14+
15+
[19f76b53-7c24-45f8-87b8-4604d0ccd248]
16+
description = "invalid isbn check digit"
17+
18+
[4164bfee-fb0a-4a1c-9f70-64c6a1903dcd]
19+
description = "valid isbn with a check digit of 10"
20+
21+
[3ed50db1-8982-4423-a993-93174a20825c]
22+
description = "check digit is a character other than X"
23+
24+
[9416f4a5-fe01-4b61-a07b-eb75892ef562]
25+
description = "invalid check digit in isbn is not treated as zero"
26+
27+
[c19ba0c4-014f-4dc3-a63f-ff9aefc9b5ec]
28+
description = "invalid character in isbn is not treated as zero"
29+
30+
[28025280-2c39-4092-9719-f3234b89c627]
31+
description = "X is only valid as a check digit"
32+
33+
[f6294e61-7e79-46b3-977b-f48789a4945b]
34+
description = "valid isbn without separating dashes"
35+
36+
[185ab99b-3a1b-45f3-aeec-b80d80b07f0b]
37+
description = "isbn without separating dashes and X as check digit"
38+
39+
[7725a837-ec8e-4528-a92a-d981dd8cf3e2]
40+
description = "isbn without check digit and dashes"
41+
42+
[47e4dfba-9c20-46ed-9958-4d3190630bdf]
43+
description = "too long isbn and no dashes"
44+
45+
[737f4e91-cbba-4175-95bf-ae630b41fb60]
46+
description = "too short isbn"
47+
48+
[5458a128-a9b6-4ff8-8afb-674e74567cef]
49+
description = "isbn without check digit"
50+
51+
[70b6ad83-d0a2-4ca7-a4d5-a9ab731800f7]
52+
description = "check digit of X should not be used for 0"
53+
54+
[94610459-55ab-4c35-9b93-ff6ea1a8e562]
55+
description = "empty isbn"
56+
57+
[7bff28d4-d770-48cc-80d6-b20b3a0fb46c]
58+
description = "input is 9 characters"
59+
60+
[ed6e8d1b-382c-4081-8326-8b772c581fec]
61+
description = "invalid characters are not ignored after checking length"
62+
63+
[daad3e58-ce00-4395-8a8e-e3eded1cdc86]
64+
description = "invalid characters are not ignored before checking length"
65+
66+
[fb5e48d8-7c03-4bfb-a088-b101df16fdc3]
67+
description = "input is too long but contains a valid isbn"
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
DROP TABLE IF EXISTS "isbn-verifier";
2+
CREATE TABLE "isbn-verifier" (
3+
isbn TEXT NOT NULL,
4+
result BOOL
5+
);
6+
7+
.mode csv
8+
.import ./data.csv "isbn-verifier"
9+
10+
UPDATE "isbn-verifier" SET result = NULL;
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
DROP TABLE IF EXISTS tests;
2+
CREATE TABLE IF NOT EXISTS tests (
3+
-- uuid and description are taken from the test.toml file
4+
uuid TEXT PRIMARY KEY,
5+
description TEXT NOT NULL,
6+
-- The following section is needed by the online test-runner
7+
status TEXT DEFAULT 'fail',
8+
message TEXT,
9+
output TEXT,
10+
test_code TEXT,
11+
task_id INTEGER DEFAULT NULL,
12+
-- Here are columns for the actual tests
13+
isbn TEXT NOT NULL,
14+
expected BOOL NOT NULL
15+
);
16+
17+
INSERT INTO tests (uuid, description, isbn, expected)
18+
VALUES
19+
('0caa3eac-d2e3-4c29-8df8-b188bc8c9292','valid isbn','3-598-21508-8',TRUE),
20+
('19f76b53-7c24-45f8-87b8-4604d0ccd248','invalid isbn check digit','3-598-21508-9',FALSE),
21+
('4164bfee-fb0a-4a1c-9f70-64c6a1903dcd','valid isbn with a check digit of 10','3-598-21507-X',TRUE),
22+
('3ed50db1-8982-4423-a993-93174a20825c','check digit is a character other than X','3-598-21507-A',FALSE),
23+
('9416f4a5-fe01-4b61-a07b-eb75892ef562','invalid check digit in isbn is not treated as zero','4-598-21507-B',FALSE),
24+
('c19ba0c4-014f-4dc3-a63f-ff9aefc9b5ec','invalid character in isbn is not treated as zero','3-598-P1581-X',FALSE),
25+
('28025280-2c39-4092-9719-f3234b89c627','X is only valid as a check digit','3-598-2X507-9',FALSE),
26+
('f6294e61-7e79-46b3-977b-f48789a4945b','valid isbn without separating dashes','3598215088',TRUE),
27+
('185ab99b-3a1b-45f3-aeec-b80d80b07f0b','isbn without separating dashes and X as check digit','359821507X',TRUE),
28+
('7725a837-ec8e-4528-a92a-d981dd8cf3e2','isbn without check digit and dashes','359821507',FALSE),
29+
('47e4dfba-9c20-46ed-9958-4d3190630bdf','too long isbn and no dashes','3598215078X',FALSE),
30+
('737f4e91-cbba-4175-95bf-ae630b41fb60','too short isbn','00',FALSE),
31+
('5458a128-a9b6-4ff8-8afb-674e74567cef','isbn without check digit','3-598-21507',FALSE),
32+
('70b6ad83-d0a2-4ca7-a4d5-a9ab731800f7','check digit of X should not be used for 0','3-598-21515-X',FALSE),
33+
('94610459-55ab-4c35-9b93-ff6ea1a8e562','empty isbn','',FALSE),
34+
('7bff28d4-d770-48cc-80d6-b20b3a0fb46c','input is 9 characters','134456729',FALSE),
35+
('ed6e8d1b-382c-4081-8326-8b772c581fec','invalid characters are not ignored after checking length','3132P34035',FALSE),
36+
('daad3e58-ce00-4395-8a8e-e3eded1cdc86','invalid characters are not ignored before checking length','3598P215088',FALSE),
37+
('fb5e48d8-7c03-4bfb-a088-b101df16fdc3','input is too long but contains a valid isbn','98245726788',FALSE);
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
"3-598-21508-8",""
2+
"3-598-21508-9",""
3+
"3-598-21507-X",""
4+
"3-598-21507-A",""
5+
"4-598-21507-B",""
6+
"3-598-P1581-X",""
7+
"3-598-2X507-9",""
8+
"3598215088",""
9+
"359821507X",""
10+
"359821507",""
11+
"3598215078X",""
12+
"00",""
13+
"3-598-21507",""
14+
"3-598-21515-X",""
15+
"",""
16+
"134456729",""
17+
"3132P34035",""
18+
"3598P215088",""
19+
"98245726788",""
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
-- Schema: CREATE TABLE "isbn-verifier" (isbn TEXT NOT NULL, result BOOL);
2+
-- Task: update the isbn-verifier table and set the result based on the isbn.
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
-- Create database:
2+
.read ./create_fixture.sql
3+
4+
-- Read user student solution and save any output as markdown in user_output.md:
5+
.mode markdown
6+
.output user_output.md
7+
.read ./isbn-verifier.sql
8+
.output
9+
10+
-- Create a clean testing environment:
11+
.read ./create_test_table.sql
12+
13+
-- Comparison of user input and the tests updates the status for each test:
14+
UPDATE tests
15+
SET status = 'pass'
16+
FROM (SELECT isbn, result FROM "isbn-verifier") AS actual
17+
WHERE (actual.isbn, actual.result) = (tests.isbn, tests.expected);
18+
19+
-- Update message for failed tests to give helpful information:
20+
UPDATE tests
21+
SET message = (
22+
'Result for "'
23+
|| tests.isbn
24+
|| '"'
25+
|| ' is <' || COALESCE(actual.result, 'NULL')
26+
|| '> but should be <' || tests.expected || '>'
27+
)
28+
FROM (SELECT isbn, result FROM "isbn-verifier") AS actual
29+
WHERE actual.isbn = tests.isbn AND tests.status = 'fail';
30+
31+
-- Save results to ./output.json (needed by the online test-runner)
32+
.mode json
33+
.once './output.json'
34+
SELECT description, status, message, output, test_code, task_id
35+
FROM tests;
36+
37+
-- Display test results in readable form for the student:
38+
.mode table
39+
SELECT description, status, message
40+
FROM tests;

0 commit comments

Comments
 (0)