From 9bd9f135af05e30da01ae893abb387b6a7ba53f5 Mon Sep 17 00:00:00 2001 From: uswebk Date: Sat, 25 Jul 2026 02:20:47 +0900 Subject: [PATCH 1/2] Add data-009 to data-013 problems for :s and :g Co-Authored-By: Claude Opus 4.8 --- problems/data/009-replace-all-delimiters.json | 53 ++++++++++++++++++ problems/data/010-strip-trailing-comma.json | 54 ++++++++++++++++++ problems/data/011-update-status-values.json | 52 ++++++++++++++++++ problems/data/012-swap-columns.json | 54 ++++++++++++++++++ problems/data/013-delete-blank-rows.json | 55 +++++++++++++++++++ 5 files changed, 268 insertions(+) create mode 100644 problems/data/009-replace-all-delimiters.json create mode 100644 problems/data/010-strip-trailing-comma.json create mode 100644 problems/data/011-update-status-values.json create mode 100644 problems/data/012-swap-columns.json create mode 100644 problems/data/013-delete-blank-rows.json diff --git a/problems/data/009-replace-all-delimiters.json b/problems/data/009-replace-all-delimiters.json new file mode 100644 index 0000000..8a2281e --- /dev/null +++ b/problems/data/009-replace-all-delimiters.json @@ -0,0 +1,53 @@ +{ + "id": "data-009", + "category": "data", + "difficulty": 2, + "start": [ + "1;alice;active", + "2;bob;active", + "3;carol;inactive" + ], + "goal": [ + "1,alice,active", + "2,bob,active", + "3,carol,inactive" + ], + "solutions": [ + { + "keys": ":%s/;/,/g", + "optimal": true + }, + { + "keys": ":1,3s/;/,/g" + } + ], + "tags": [ + ":s", + "substitute", + "csv" + ], + "i18n": { + "ja": { + "title": "区切り文字を一括で置換する", + "description": "すべてのセミコロンをカンマに置き換えてください", + "hints": [ + ":%s/old/new/g は全行のすべての一致を置換する" + ], + "notes": [ + ":%s/;/,/g の % は全行、末尾の g は行内のすべての一致を意味する", + ":1,3s/;/,/g のように範囲を数字で指定しても同じ結果になる" + ] + }, + "en": { + "title": "Replace every delimiter", + "description": "Replace every semicolon with a comma", + "hints": [ + ":%s/old/new/g substitutes every match on every line" + ], + "notes": [ + "In :%s/;/,/g the % means all lines and the trailing g means every match in each line", + "An explicit range like :1,3s/;/,/g does the same thing" + ] + } + } +} diff --git a/problems/data/010-strip-trailing-comma.json b/problems/data/010-strip-trailing-comma.json new file mode 100644 index 0000000..94891b7 --- /dev/null +++ b/problems/data/010-strip-trailing-comma.json @@ -0,0 +1,54 @@ +{ + "id": "data-010", + "category": "data", + "difficulty": 2, + "start": [ + "id,name,", + "1,alice,", + "2,bob," + ], + "goal": [ + "id,name", + "1,alice", + "2,bob" + ], + "solutions": [ + { + "keys": ":%s/,$//", + "optimal": true + }, + { + "keys": ":%normal $x" + } + ], + "tags": [ + ":s", + "substitute", + "regex", + "csv" + ], + "i18n": { + "ja": { + "title": "行末のカンマを消す", + "description": "各行の末尾のカンマを削除してください", + "hints": [ + "置換パターンの $ は行末にマッチする" + ], + "notes": [ + ":%s/,$// は全行の行末カンマだけを空文字に置換する", + ":%normal $x で全行に対して $x(行末へ移動して1文字削除)を実行しても同じ" + ] + }, + "en": { + "title": "Strip the trailing comma", + "description": "Delete the trailing comma on every line", + "hints": [ + "$ in a substitute pattern matches the end of the line" + ], + "notes": [ + ":%s/,$// replaces only the end-of-line comma with nothing on every line", + ":%normal $x runs $x (jump to end of line, delete one char) on every line for the same result" + ] + } + } +} diff --git a/problems/data/011-update-status-values.json b/problems/data/011-update-status-values.json new file mode 100644 index 0000000..73b5acc --- /dev/null +++ b/problems/data/011-update-status-values.json @@ -0,0 +1,52 @@ +{ + "id": "data-011", + "category": "data", + "difficulty": 2, + "start": [ + "1,alice,pending", + "2,bob,done", + "3,carol,pending" + ], + "goal": [ + "1,alice,done", + "2,bob,done", + "3,carol,done" + ], + "solutions": [ + { + "keys": ":%s/pending/done/", + "optimal": true + }, + { + "keys": ":%s/pending/done/g" + } + ], + "tags": [ + ":s", + "substitute" + ], + "i18n": { + "ja": { + "title": "ステータスをまとめて更新する", + "description": "すべての pending を done に置き換えてください", + "hints": [ + "1行に1回しか出てこない語なら g フラグは要らない" + ], + "notes": [ + ":%s/pending/done/ は各行の最初の一致だけを置換する。1行1回なのでこれで十分", + "g フラグを付けても結果は同じだが、この行では不要" + ] + }, + "en": { + "title": "Update the status values", + "description": "Replace every 'pending' with 'done'", + "hints": [ + "The g flag is unnecessary when the word appears once per line" + ], + "notes": [ + ":%s/pending/done/ substitutes the first match on each line, which is enough here", + "Adding the g flag gives the same result but is not needed on these lines" + ] + } + } +} diff --git a/problems/data/012-swap-columns.json b/problems/data/012-swap-columns.json new file mode 100644 index 0000000..4ac905e --- /dev/null +++ b/problems/data/012-swap-columns.json @@ -0,0 +1,54 @@ +{ + "id": "data-012", + "category": "data", + "difficulty": 3, + "start": [ + "alice,1", + "bob,2", + "carol,3" + ], + "goal": [ + "1,alice", + "2,bob", + "3,carol" + ], + "solutions": [ + { + "keys": ":%s/\\(\\w\\+\\),\\(\\w\\+\\)/\\2,\\1/", + "optimal": true + }, + { + "keys": ":%s/^\\([^,]*\\),\\(.*\\)$/\\2,\\1/" + } + ], + "tags": [ + ":s", + "substitute", + "regex", + "capture-group" + ], + "i18n": { + "ja": { + "title": "2つの列を入れ替える", + "description": "名前と数値の列を入れ替えて 1,alice の並びにしてください", + "hints": [ + "\\( \\) で囲んだ部分は置換後に \\1 \\2 で参照できる" + ], + "notes": [ + "\\(\\w\\+\\),\\(\\w\\+\\) で2つの列を捕まえ、\\2,\\1 で順番を逆にして書き戻す", + "[^,]* と .* を使っても同じように列を捕まえられる" + ] + }, + "en": { + "title": "Swap the two columns", + "description": "Swap the name and number columns so each line reads '1,alice'", + "hints": [ + "Groups wrapped in \\( \\) can be referenced as \\1 and \\2 in the replacement" + ], + "notes": [ + "\\(\\w\\+\\),\\(\\w\\+\\) captures both columns and \\2,\\1 writes them back in reverse order", + "[^,]* and .* capture the same two columns" + ] + } + } +} diff --git a/problems/data/013-delete-blank-rows.json b/problems/data/013-delete-blank-rows.json new file mode 100644 index 0000000..8c98009 --- /dev/null +++ b/problems/data/013-delete-blank-rows.json @@ -0,0 +1,55 @@ +{ + "id": "data-013", + "category": "data", + "difficulty": 3, + "start": [ + "1,alice", + "", + "2,bob", + "", + "3,carol" + ], + "goal": [ + "1,alice", + "2,bob", + "3,carol" + ], + "solutions": [ + { + "keys": ":g/^$/d", + "optimal": true + }, + { + "keys": "jddjdd" + } + ], + "tags": [ + ":g", + "global", + "dd" + ], + "i18n": { + "ja": { + "title": "空行を取り除く", + "description": "データの間にある空行をすべて削除してください", + "hints": [ + ":g/pattern/d はパターンに一致する行をすべて削除する" + ], + "notes": [ + ":g/^$/d は空行(行頭がすぐ行末の行)だけを対象に d を実行する", + "空行が少なければ j で移動して dd を繰り返してもよい" + ] + }, + "en": { + "title": "Remove the blank rows", + "description": "Delete every blank line between the data rows", + "hints": [ + ":g/pattern/d deletes every line matching the pattern" + ], + "notes": [ + ":g/^$/d runs d on every empty line (start of line immediately followed by end of line)", + "With only a couple of blanks you can also just move with j and repeat dd" + ] + } + } +} From 272578ffe1d3655f4f03d90e96087ac4a1154fae Mon Sep 17 00:00:00 2001 From: uswebk Date: Mon, 27 Jul 2026 01:43:35 +0900 Subject: [PATCH 2/2] Drop the column-swap problem and renumber data-013 data-012 (capture-group column swap) was far harder than the rest of the data set, so remove it and shift delete-blank-rows down to 012 to keep the ids contiguous. Co-Authored-By: Claude Opus 5 --- ...k-rows.json => 012-delete-blank-rows.json} | 2 +- problems/data/012-swap-columns.json | 54 ------------------- 2 files changed, 1 insertion(+), 55 deletions(-) rename problems/data/{013-delete-blank-rows.json => 012-delete-blank-rows.json} (98%) delete mode 100644 problems/data/012-swap-columns.json diff --git a/problems/data/013-delete-blank-rows.json b/problems/data/012-delete-blank-rows.json similarity index 98% rename from problems/data/013-delete-blank-rows.json rename to problems/data/012-delete-blank-rows.json index 8c98009..7b5aca6 100644 --- a/problems/data/013-delete-blank-rows.json +++ b/problems/data/012-delete-blank-rows.json @@ -1,5 +1,5 @@ { - "id": "data-013", + "id": "data-012", "category": "data", "difficulty": 3, "start": [ diff --git a/problems/data/012-swap-columns.json b/problems/data/012-swap-columns.json deleted file mode 100644 index 4ac905e..0000000 --- a/problems/data/012-swap-columns.json +++ /dev/null @@ -1,54 +0,0 @@ -{ - "id": "data-012", - "category": "data", - "difficulty": 3, - "start": [ - "alice,1", - "bob,2", - "carol,3" - ], - "goal": [ - "1,alice", - "2,bob", - "3,carol" - ], - "solutions": [ - { - "keys": ":%s/\\(\\w\\+\\),\\(\\w\\+\\)/\\2,\\1/", - "optimal": true - }, - { - "keys": ":%s/^\\([^,]*\\),\\(.*\\)$/\\2,\\1/" - } - ], - "tags": [ - ":s", - "substitute", - "regex", - "capture-group" - ], - "i18n": { - "ja": { - "title": "2つの列を入れ替える", - "description": "名前と数値の列を入れ替えて 1,alice の並びにしてください", - "hints": [ - "\\( \\) で囲んだ部分は置換後に \\1 \\2 で参照できる" - ], - "notes": [ - "\\(\\w\\+\\),\\(\\w\\+\\) で2つの列を捕まえ、\\2,\\1 で順番を逆にして書き戻す", - "[^,]* と .* を使っても同じように列を捕まえられる" - ] - }, - "en": { - "title": "Swap the two columns", - "description": "Swap the name and number columns so each line reads '1,alice'", - "hints": [ - "Groups wrapped in \\( \\) can be referenced as \\1 and \\2 in the replacement" - ], - "notes": [ - "\\(\\w\\+\\),\\(\\w\\+\\) captures both columns and \\2,\\1 writes them back in reverse order", - "[^,]* and .* capture the same two columns" - ] - } - } -}