Skip to content

Commit f1f998c

Browse files
authored
Add new practice exercise: anagram (#105)
1 parent ad68315 commit f1f998c

File tree

12 files changed

+321
-0
lines changed

12 files changed

+321
-0
lines changed

config.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,14 @@
194194
"prerequisites": [],
195195
"difficulty": 8
196196
},
197+
{
198+
"slug": "anagram",
199+
"name": "Anagram",
200+
"uuid": "a4231e93-2e2c-4db2-a073-2b2c30b40185",
201+
"practices": [],
202+
"prerequisites": [],
203+
"difficulty": 8
204+
},
197205
{
198206
"slug": "armstrong-numbers",
199207
"name": "Armstrong Numbers",
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# SQLite-specific instructions
2+
3+
* The **candidates** column contains a JSON-encoded list of strings.
4+
Example:
5+
```json
6+
["stone","tones","banana","tons","notes","Seton"]
7+
```
8+
* The **result** column should contain JSON-encoded list of strings as well.
9+
10+
## Table Schema
11+
12+
```sql
13+
CREATE TABLE anagram (
14+
subject TEXT NOT NULL,
15+
candidates TEXT NOT NULL, -- json array of strings
16+
result TEXT -- json array of strings
17+
);
18+
```
19+
20+
## JSON documentation
21+
22+
[JSON Functions And Operators][json-docs]
23+
24+
[json-docs]: https://www.sqlite.org/json1.htm
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Instructions
2+
3+
Given a target word and one or more candidate words, your task is to find the candidates that are anagrams of the target.
4+
5+
An anagram is a rearrangement of letters to form a new word: for example `"owns"` is an anagram of `"snow"`.
6+
A word is _not_ its own anagram: for example, `"stop"` is not an anagram of `"stop"`.
7+
8+
The target word and candidate words are made up of one or more ASCII alphabetic characters (`A`-`Z` and `a`-`z`).
9+
Lowercase and uppercase characters are equivalent: for example, `"PoTS"` is an anagram of `"sTOp"`, but `"StoP"` is not an anagram of `"sTOp"`.
10+
The words you need to find should be taken from the candidate words, using the same letter case.
11+
12+
Given the target `"stone"` and the candidate words `"stone"`, `"tones"`, `"banana"`, `"tons"`, `"notes"`, and `"Seton"`, the anagram words you need to find are `"tones"`, `"notes"`, and `"Seton"`.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Introduction
2+
3+
At a garage sale, you find a lovely vintage typewriter at a bargain price!
4+
Excitedly, you rush home, insert a sheet of paper, and start typing away.
5+
However, your excitement wanes when you examine the output: all words are garbled!
6+
For example, it prints "stop" instead of "post" and "least" instead of "stale."
7+
Carefully, you try again, but now it prints "spot" and "slate."
8+
After some experimentation, you find there is a random delay before each letter is printed, which messes up the order.
9+
You now understand why they sold it for so little money!
10+
11+
You realize this quirk allows you to generate anagrams, which are words formed by rearranging the letters of another word.
12+
Pleased with your finding, you spend the rest of the day generating hundreds of anagrams.
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+
"anagram.sql"
8+
],
9+
"test": [
10+
"anagram_test.sql"
11+
],
12+
"example": [
13+
".meta/example.sql"
14+
]
15+
},
16+
"blurb": "Given a word and a list of possible anagrams, select the correct sublist.",
17+
"source": "Inspired by the Extreme Startup game",
18+
"source_url": "https://github.com/rchatley/extreme_startup"
19+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
DROP TABLE IF EXISTS tmp;
2+
CREATE TEMPORARY TABLE tmp AS
3+
SELECT CAST(word AS TEXT) word FROM (
4+
SELECT LOWER(subject) word FROM anagram
5+
UNION
6+
SELECT LOWER(j.VALUE)
7+
FROM anagram, JSON_EACH(candidates) j
8+
);
9+
ALTER TABLE tmp ADD s TEXT;
10+
UPDATE tmp
11+
SET s = (
12+
WITH RECURSIVE rcte(word,c) AS (
13+
VALUES(word, '')
14+
UNION ALL
15+
SELECT SUBSTR(word, 2), SUBSTR(word, 1, 1)
16+
FROM rcte
17+
WHERE LENGTH(word) > 0
18+
)
19+
SELECT GROUP_CONCAT(c, '')
20+
FROM (
21+
SELECT c
22+
FROM rcte
23+
WHERE UNICODE(c) BETWEEN UNICODE('a') AND UNICODE('z')
24+
ORDER BY c
25+
)
26+
);
27+
28+
UPDATE anagram
29+
SET result = (
30+
SELECT JSON_GROUP_ARRAY(candidate)
31+
FROM (
32+
SELECT subject, candidate,
33+
(SELECT s FROM tmp WHERE LOWER(subject) = word) AS a,
34+
(SELECT s FROM tmp WHERE LOWER(candidate) = word) AS b
35+
FROM (
36+
SELECT subject,
37+
j.VALUE AS candidate
38+
FROM JSON_EACH(candidates) AS j
39+
)
40+
)
41+
WHERE a = b
42+
AND LOWER(subject) <> LOWER(candidate)
43+
);
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
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+
[dd40c4d2-3c8b-44e5-992a-f42b393ec373]
13+
description = "no matches"
14+
15+
[b3cca662-f50a-489e-ae10-ab8290a09bdc]
16+
description = "detects two anagrams"
17+
include = false
18+
19+
[03eb9bbe-8906-4ea0-84fa-ffe711b52c8b]
20+
description = "detects two anagrams"
21+
reimplements = "b3cca662-f50a-489e-ae10-ab8290a09bdc"
22+
23+
[a27558ee-9ba0-4552-96b1-ecf665b06556]
24+
description = "does not detect anagram subsets"
25+
26+
[64cd4584-fc15-4781-b633-3d814c4941a4]
27+
description = "detects anagram"
28+
29+
[99c91beb-838f-4ccd-b123-935139917283]
30+
description = "detects three anagrams"
31+
32+
[78487770-e258-4e1f-a646-8ece10950d90]
33+
description = "detects multiple anagrams with different case"
34+
35+
[1d0ab8aa-362f-49b7-9902-3d0c668d557b]
36+
description = "does not detect non-anagrams with identical checksum"
37+
38+
[9e632c0b-c0b1-4804-8cc1-e295dea6d8a8]
39+
description = "detects anagrams case-insensitively"
40+
41+
[b248e49f-0905-48d2-9c8d-bd02d8c3e392]
42+
description = "detects anagrams using case-insensitive subject"
43+
44+
[f367325c-78ec-411c-be76-e79047f4bd54]
45+
description = "detects anagrams using case-insensitive possible matches"
46+
47+
[7cc195ad-e3c7-44ee-9fd2-d3c344806a2c]
48+
description = "does not detect an anagram if the original word is repeated"
49+
include = false
50+
51+
[630abb71-a94e-4715-8395-179ec1df9f91]
52+
description = "does not detect an anagram if the original word is repeated"
53+
reimplements = "7cc195ad-e3c7-44ee-9fd2-d3c344806a2c"
54+
55+
[9878a1c9-d6ea-4235-ae51-3ea2befd6842]
56+
description = "anagrams must use all letters exactly once"
57+
58+
[85757361-4535-45fd-ac0e-3810d40debc1]
59+
description = "words are not anagrams of themselves (case-insensitive)"
60+
include = false
61+
62+
[68934ed0-010b-4ef9-857a-20c9012d1ebf]
63+
description = "words are not anagrams of themselves"
64+
reimplements = "85757361-4535-45fd-ac0e-3810d40debc1"
65+
66+
[589384f3-4c8a-4e7d-9edc-51c3e5f0c90e]
67+
description = "words are not anagrams of themselves even if letter case is partially different"
68+
reimplements = "85757361-4535-45fd-ac0e-3810d40debc1"
69+
70+
[ba53e423-7e02-41ee-9ae2-71f91e6d18e6]
71+
description = "words are not anagrams of themselves even if letter case is completely different"
72+
reimplements = "85757361-4535-45fd-ac0e-3810d40debc1"
73+
74+
[a0705568-628c-4b55-9798-82e4acde51ca]
75+
description = "words other than themselves can be anagrams"
76+
include = false
77+
78+
[33d3f67e-fbb9-49d3-a90e-0beb00861da7]
79+
description = "words other than themselves can be anagrams"
80+
reimplements = "a0705568-628c-4b55-9798-82e4acde51ca"
81+
82+
[a6854f66-eec1-4afd-a137-62ef2870c051]
83+
description = "handles case of greek letters"
84+
include = false
85+
86+
[fd3509e5-e3ba-409d-ac3d-a9ac84d13296]
87+
description = "different characters may have the same bytes"
88+
include = false
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
-- Schema: CREATE TABLE anagram (
2+
-- subject TEXT NOT NULL,
3+
-- candidates TEXT NOT NULL, -- json array of strings
4+
-- result TEXT -- json array of strings
5+
-- );
6+
7+
-- Task: update the anagram table and set the result based on valid
8+
-- candidates for the subject field
9+
-- * the candidates column contains a JSON-encoded list of strings.
10+
-- * the result column should contain JSON-encoded list of
11+
-- strings as well.
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
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 ./anagram.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 subject, candidates, result FROM anagram) AS actual
17+
WHERE (actual.subject, actual.candidates, JSON(actual.result)) = (tests.subject, tests.candidates, JSON(tests.expected));
18+
19+
-- Update message for failed tests to give helpful information:
20+
UPDATE tests
21+
SET message = (
22+
'Result for "'
23+
|| JSON_OBJECT(
24+
'subject', tests.subject,
25+
'candidates', JSON(tests.candidates)
26+
)
27+
|| '"'
28+
|| ' is <' || COALESCE(actual.result, 'NULL')
29+
|| '> but should be <' || tests.expected || '>'
30+
)
31+
FROM (SELECT subject, candidates, result FROM anagram) AS actual
32+
WHERE (actual.subject, actual.candidates) = (tests.subject, tests.candidates) AND tests.status = 'fail';
33+
34+
-- Save results to ./output.json (needed by the online test-runner)
35+
.mode json
36+
.once './output.json'
37+
SELECT description, status, message, output, test_code, task_id
38+
FROM tests;
39+
40+
-- Display test results in readable form for the student:
41+
.mode table
42+
SELECT description, status, message
43+
FROM tests;
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
DROP TABLE IF EXISTS anagram;
2+
CREATE TABLE anagram (
3+
subject TEXT NOT NULL,
4+
candidates TEXT NOT NULL, -- json array of strings
5+
result TEXT -- json array of strings
6+
);
7+
.mode csv
8+
.import ./data.csv anagram
9+
10+
UPDATE anagram SET result = NULL;

0 commit comments

Comments
 (0)