大量のテキストデータから重複した行を手作業で削除するのは本当に大変ですよね。
「Excelやスプレッドシートに貼り付けて関数を使うのも面倒…」
「専用ソフトをインストールするのはちょっとハードルが高い…」
そんな悩みを解決するのが、今回ご紹介する 「重複行削除ツール」 です。
この記事では、このツールを たった1つのHTMLファイル で自作する方法をわかりやすく解説します。
しかも、インストールや特別な環境構築は不要。ブラウザ完結型なので、作ったその場で誰でもすぐに使えます。
この記事を読み終える頃には、あなただけのオリジナル重複行削除ツールが完成し、
「自分で作れた!」という達成感も味わえます。
さあ、一緒に プログラミングの第一歩 を踏み出してみましょう!
プログラミング初心者でも大丈夫。コピペから始めれば、必ず動くツールが完成します!

完成イメージをチェック!|重複行削除ツールの概要
まずは、この記事で作る 重複行削除ツール の完成イメージを見てみましょう。
このツールは、テキストを貼り付けるだけで 重複行を自動で検出 し、不要な行をクリック操作で削除できる便利な仕組みです。
しかも、使うのは HTMLとJavaScriptだけ。サーバーや特別な環境は一切不要で、ブラウザがあれば誰でもすぐに動かせます。
このツールでできること
- 入力エリア
テキストを貼り付けるだけで、重複行をリアルタイム検出。 - 重複行リスト
重複している行を一覧表示。空行も「(空行)」として表示されるので、空行の削除も簡単です。 - ボタン操作
- すべて選択 / すべて解除:大量の重複行も一括で操作
- 重複行を削除:選択した行だけを削除し、出力に反映
- 出力結果をコピー:ワンクリックでクリップボードに保存
- クリア:入力内容と結果をリセット
- シンプルで直感的なデザイン
複雑な設定はなく、誰でもすぐに理解して使えるUI。
【実際の重複行削除ツールの動作を試してみたい方はこちらからどうぞ】▼
ツールの作り方(コピペで完成!)
それでは、いよいよ実際に 重複行削除ツール を作ってみましょう。必要なのは、たった1つのファイルだけ。専門的な環境や複雑な準備は一切不要です。
ステップ1:HTMLファイルを作成しよう
- パソコンで新しいテキストファイルを作成します。
- ファイル名は
duplicate-remover.html
がおすすめです。 - これから提示するソースコード全文をコピーし、このファイルに貼り付けましょう。
今回のコードは、HTML・CSS・JavaScriptをすべて1つのファイルにまとめたものなので、プログラミング初心者でもすぐに試せます。
ソースコード全文
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 |
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>重複行の可視化と選択削除ツール</title> <style> :root { --main-bg-color: #f4f4f9; --text-color: #333333; --container-bg-color: #ffffff; --box-shadow-color: rgba(0, 0, 0, 0.1); --border-color: #dddddd; --button-color: #4a90e2; --button-hover-color: #357bd2; --input-bg-color: #fafafa; } @media (prefers-color-scheme: dark) { :root { --main-bg-color: #1e1e2f; --text-color: #e0e0e0; --container-bg-color: #2a2a3e; --box-shadow-color: rgba(0, 0, 0, 0.2); --border-color: #444455; --button-color: #5d5d81; --button-hover-color: #7b7b9e; --input-bg-color: #333345; } } body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; background-color: var(--main-bg-color); color: var(--text-color); margin: 0; padding: 20px; display: flex; justify-content: center; align-items: flex-start; min-height: 100vh; box-sizing: border-box; line-height: 1.6; } .container { width: 100%; max-width: 900px; background-color: var(--container-bg-color); padding: 30px; border-radius: 12px; box-shadow: 0 4px 12px var(--box-shadow-color); text-align: center; } h1 { font-size: 1.8rem; margin-bottom: 10px; } p { margin-bottom: 20px; color: #777; } .tool-section { display: flex; flex-direction: column; gap: 20px; } .main-group { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; align-items: flex-start; } @media (max-width: 768px) { .main-group { grid-template-columns: 1fr; } } textarea { width: 100%; height: 250px; padding: 15px; font-size: 1rem; border: 1px solid var(--border-color); border-radius: 8px; resize: vertical; box-sizing: border-box; background-color: var(--input-bg-color); color: var(--text-color); transition: border-color 0.3s; } textarea:focus { border-color: var(--button-color); outline: none; } .input-area, .output-area, .duplicate-area { text-align: left; } .label-bold { font-weight: bold; } .duplicate-actions { display: flex; gap: 10px; } .duplicate-area-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px; } .duplicate-area-inner { height: 200px; overflow-y: auto; border: 1px solid var(--border-color); border-radius: 8px; padding: 15px; background-color: var(--input-bg-color); } .duplicate-item { margin-bottom: 8px; display: flex; align-items: flex-start; } .duplicate-item label { display: flex; align-items: center; cursor: pointer; } .duplicate-item input[type="checkbox"] { margin-right: 10px; min-width: 18px; height: 18px; cursor: pointer; } .duplicate-text { word-break: break-all; flex-grow: 1; } .duplicate-count { font-size: 0.8rem; color: #999; margin-left: 10px; white-space: nowrap; } .button-group { display: flex; justify-content: center; gap: 15px; margin-top: 20px; flex-wrap: wrap; } .action-btn, .select-btn { padding: 12px 25px; font-size: 1rem; font-weight: bold; border: none; border-radius: 8px; cursor: pointer; transition: background-color 0.3s, transform 0.1s; } .select-btn { font-size: 0.9rem; padding: 8px 15px; background-color: #ddd; color: #333; } .select-btn:hover { background-color: #ccc; } .process-btn { background-color: #28a745; color: white; } .process-btn:hover { background-color: #218838; } .clear-btn { background-color: #dc3545; color: white; } .clear-btn:hover { background-color: #c82333; } .copy-btn { background-color: var(--button-color); color: white; } .copy-btn:hover { background-color: var(--button-hover-color); } .action-btn:active, .select-btn:active { transform: scale(0.98); } .message { margin-top: 10px; color: #28a745; font-weight: bold; min-height: 20px; } </style> </head> <body> <div class="container"> <h1>重複行の可視化と選択削除ツール</h1> <p>テキスト内の重複した行を特定し、選択して削除できます。</p> <div class="tool-section"> <div class="main-group"> <div class="input-area"> <label for="inputText" class="label-bold">入力エリア</label> <textarea id="inputText" placeholder="ここにテキストを貼り付けてください..."></textarea> </div> <div class="duplicate-area"> <div class="duplicate-area-header"> <label class="label-bold">重複行リスト</label> <div class="duplicate-actions"> <button id="selectAllBtn" class="select-btn">すべて選択</button> <button id="deselectAllBtn" class="select-btn">すべて解除</button> </div> </div> <div id="duplicateList" class="duplicate-area-inner"> </div> </div> </div> <div class="button-group"> <button id="removeDuplicatesBtn" class="action-btn process-btn">重複行を削除</button> <button id="copyBtn" class="action-btn copy-btn">出力結果をコピー</button> <button id="clearBtn" class="action-btn clear-btn">クリア</button> </div> <div class="output-area"> <label for="outputText" class="label-bold">出力エリア</label> <textarea id="outputText" readonly></textarea> <div id="message" class="message"></div> </div> </div> </div> <script> const inputText = document.getElementById('inputText'); const outputText = document.getElementById('outputText'); const duplicateList = document.getElementById('duplicateList'); const selectAllBtn = document.getElementById('selectAllBtn'); const deselectAllBtn = document.getElementById('deselectAllBtn'); const removeDuplicatesBtn = document.getElementById('removeDuplicatesBtn'); const copyBtn = document.getElementById('copyBtn'); const clearBtn = document.getElementById('clearBtn'); const message = document.getElementById('message'); function findDuplicates() { const lines = inputText.value.split(/\r?\n/); const counts = {}; lines.forEach(line => { counts[line] = (counts[line] || 0) + 1; }); const duplicates = []; const seenDuplicates = new Set(); lines.forEach(line => { if (counts[line] > 1 && !seenDuplicates.has(line)) { duplicates.push({ text: line, count: counts[line] }); seenDuplicates.add(line); } }); duplicateList.innerHTML = ''; if (duplicates.length > 0) { duplicates.forEach(item => { const listItem = document.createElement('div'); listItem.className = 'duplicate-item'; const displayText = item.text === '' ? '(空行)' : item.text; listItem.innerHTML = ` <label> <input type="checkbox" data-line="${item.text}"> <span class="duplicate-text">${displayText}</span> <span class="duplicate-count">(${item.count}回)</span> </label> `; duplicateList.appendChild(listItem); }); } else if (inputText.value.length > 0) { duplicateList.innerHTML = `<p style="text-align:center; color:#999; margin-top:50px;">重複行は見つかりませんでした。</p>`; } else { duplicateList.innerHTML = ''; } } function processAndRemoveSelected() { const lines = inputText.value.split(/\r?\n/); const checkboxes = duplicateList.querySelectorAll('input[type="checkbox"]'); const linesToRemove = new Set(); checkboxes.forEach(checkbox => { if (checkbox.checked) { linesToRemove.add(checkbox.dataset.line); } }); const newLines = lines.filter(line => !linesToRemove.has(line)); outputText.value = newLines.join('\n'); showMessage(`${lines.length - newLines.length}行の重複行を削除しました。`); } function showMessage(msg) { message.textContent = msg; setTimeout(() => { message.textContent = ''; }, 3000); } inputText.addEventListener('input', () => { findDuplicates(); outputText.value = ''; }); selectAllBtn.addEventListener('click', () => { duplicateList.querySelectorAll('input[type="checkbox"]').forEach(cb => cb.checked = true); }); deselectAllBtn.addEventListener('click', () => { duplicateList.querySelectorAll('input[type="checkbox"]').forEach(cb => cb.checked = false); }); removeDuplicatesBtn.addEventListener('click', processAndRemoveSelected); copyBtn.addEventListener('click', () => { if (outputText.value) { navigator.clipboard.writeText(outputText.value).then(() => { showMessage('出力結果をコピーしました!'); }).catch(err => { console.error('コピーに失敗しました', err); showMessage('コピーに失敗しました。'); }); } }); clearBtn.addEventListener('click', () => { inputText.value = ''; outputText.value = ''; findDuplicates(); showMessage('入力エリアをクリアしました。'); }); findDuplicates(); </script> </body> </html> |
ステップ2:ブラウザで開いてみよう
- ファイルを保存したら、あとはブラウザで開くだけです。
- 保存した
duplicate-remover.html
を、ダブルクリック するか、ブラウザにドラッグ&ドロップして開きます。 - すると、ツールの画面が表示され、すぐに利用できるようになります。
これで、あなただけの オリジナル重複行削除ツール が完成です!
このツールはインターネット接続がなくても動くため、パソコンに保存しておけばオフライン環境でも利用できます。
徹底解説:重複行削除ツールのコードの仕組み(初心者でも安心!)
ここからは「なぜ、あのコードをコピー&ペーストするだけでツールが動いたのか?」を一つずつ解説していきます。専門用語も、できるだけ噛み砕いて説明するので安心してください。
HTML|ツールの「骨組み」を作る
HTMLは、ウェブページの骨組みを作る言語です。ツールの入力欄やボタン、出力エリアなど、すべてのパーツはHTMLで定義されています。
1 2 3 |
<div class="container"> <textarea id="inputText"></textarea> <button id="removeDuplicatesBtn" class="action-btn process-btn">重複行を削除</button> |
<div class="container">
→ 全体をまとめる「大きな箱」<textarea>
→ テキストを入力する欄。id="inputText"
という名前を付けることで、JavaScriptから操作可能に<button>
→ クリックできるボタン。id="removeDuplicatesBtn"
により、削除処理とひも付け
CSS|ツールの「見た目」を整える
CSSは、HTMLで作った骨組みに色・配置・形などのデザインを加える役割を持ちます。
1 2 3 4 |
.container { display: grid; grid-template-columns: 1fr 1fr; } |
display: grid
→ レイアウトを柔軟に調整する仕組みgrid-template-columns: 1fr 1fr
→ 画面を2分割し、入力エリアとリストを横並びに
JavaScript|ツールに「頭脳」を与える
JavaScriptは、ツールを実際に動かす頭脳です。入力内容を処理し、重複行を検出したり削除したりする役割を担います。
基本の仕組み
1 |
const inputText = document.getElementById('inputText'); |
→ 入力欄を「inputText」という変数に保存して操作可能に。
1 |
inputText.addEventListener('input', findDuplicates); |
→ 入力があるたびに findDuplicates()
関数を呼び出し、重複行を探す処理を実行。
重複検出の仕組み
1 |
inputText.value.split(/\r?\n/); |
→ 入力テキストを「改行」で分割し、1行ずつのリストに変換。
1 |
counts[line] = (counts[line] || 0) + 1; |
→ 行ごとにカウントして、出現回数を記録。
1 |
new Set() |
→ 重複を許さない特別なリスト。同じ行が繰り返し出ないように管理。
削除処理の仕組み
1 |
lines.filter(line => !linesToRemove.has(line)); |
→ 元の行の中から「削除対象に含まれていない行」だけを残し、新しいテキストを作成。
さらにステップアップ!重複行削除ツールのカスタマイズヒント
今回作ったツールは「第一歩」にすぎません。
次のステップは、自分好みの機能を追加して さらに使いやすく改造すること です。
実は、少しの工夫でいろいろな拡張が可能になります。
アイデア1:「重複しない行だけを抽出する」機能
現状は「重複行を削除する」機能ですが、逆に 「一度しか登場しない行だけを抜き出す」 機能も便利です。
ヒント:
findDuplicates()
関数内で、出現回数が1の行を別の配列に保存- 新しいボタンを追加し、その配列を
outputText
に表示
アイデア2:空白や大文字・小文字を無視する
「apple」と「Apple」や、「 banana」(空白あり)と「banana」を同じものとして扱いたい場面はよくあります。
ヒント:
trim()
→ 前後の余分な空白を削除toLowerCase()
→ すべて小文字に変換
こうした小さなカスタマイズに挑戦することで、プログラミングスキルは一気にレベルアップします。
ぜひ、あなたの重複行削除ツールを育てながら「自分だけの便利機能」を追加してみてください!
おすすめの学習リソース|もっとスキルを伸ばしたいあなたへ
「重複行削除ツール」を完成させたことで、あなたはすでに HTML・CSS・JavaScriptの基礎 に触れました。ここからさらにステップアップするために役立つ学習リソースを紹介します。
おすすめ書籍
『確かな力が身につくJavaScript超入門』(SBクリエイティブ)
- JavaScriptを基礎から丁寧に学べる入門書。
- 実践的なサンプルも豊富で、今回のツールのような小さなアプリを自分で作れるようになります。
- 初学者にぴったりの1冊です。
『スラスラわかるHTML&CSSのきほん』(SBクリエイティブ)
- Web制作初心者に最適な入門書。HTMLとCSSの仕組みをやさしく解説。
- これからWebページを作ってみたい方にぴったり。
- レイアウトの基本やスタイルの調整方法など、実践的に学べます。
オンライン講座編
Udemy|世界最大級のオンライン学習プラットフォーム
世界中で利用されるオンライン学習サイト。
HTML、CSS、JavaScriptの入門から応用まで、高評価の講座が数百種類揃っています。
初心者でも動画を見ながら手を動かせるので、挫折しにくいのが魅力です。
Udemyおすすめ講座
【HTML,CSS,JS,PHP,Git,Docker】プログラミング初心者OK! ゼロからわかるWebシステム開発
- 実際に手を動かしながら学べる実践型講座。
- 初心者でも、コードの意味が理解できるよう丁寧に解説されています。
- 自作ツールのカスタマイズや新機能追加にも応用可能。
他にも講座を探したい方はこちら → UdemyのWeb開発カテゴリー講座
まとめ|重複行削除ツールを自作して得られるもの
今回は、HTMLとJavaScriptだけで動く重複行削除ツールの作り方を紹介しました。
- 入力されたテキストから重複行を検出
- チェックで不要な行を削除
- ワンクリックでコピー可能
- しかもブラウザ完結、オフラインでも利用可能
この体験を通じて、プログラミングは特別な人だけのものではないと感じてもらえたら嬉しいです。
今日からあなたも、身近な不便を解決するツールを自作できる開発者の仲間入りです。
次のステップは、この記事で紹介した「カスタマイズのヒント」に挑戦したり、学習リソースで知識を深めたりすること。
小さな一歩を積み重ねることで、きっと大きな成長につながります。
ぜひ、あなたの手でさらに便利なオリジナルツールを育てていってください。
最初はコピペで十分。そこから少しずつ“仕組み”を理解すればいいですよ

関連記事
【実際の重複行削除ツールの動作を試してみたい方はこちらからどうぞ】▼
【作り方記事】改行・空白・タブ削除ツールの作り方|HTMLとJavaScriptで簡単実装 ▼
【作り方記事】全角半角&ひらがなカタカナ変換ツールの作り方|HTMLとJavaScriptで簡単実装 ▼