Paizaの問題「宝くじ当選判定」を解いてみたら、最初に書いたコードが動かなかったんですよね……。試行錯誤してシンプルで読みやすいコードに改善したので、初心者の方にも分かりやすいようにシェアします!
🎰 宝くじのルール
宝くじには 100000 以上 199999 以下の6桁の番号がついています。当選番号が 142358 だった場合、以下のように当たりが決まります:
1等:完全一致 (例: 142358)
前後賞:当選番号の前後 (例: 142357, 142359)
2等:下4桁が一致 (例: x42358)
3等:下3桁が一致 (例: xxx358)
外れ:上記以外
入力例:
142358
3
195283
167358
142359
出力例:
blank
third
adjacent
では、実際にコードを書いていきましょう!
🚨 初めに書いたコード(NG例)
const rl = require('readline').createInterface({ input: process.stdin });
const lines = [];
rl.on('line', (input) => {
lines.push(input);
});
rl.on('close', () => {
const winningNumber = Number(lines[0]); // 一等の当選番号
const ticketCount = Number(lines[1]); // 宝くじの枚数
const lotteryNumbers = lines.slice(2).map(Number); // 宝くじの番号
const winningNumber_4 = String(winningNumber).match(/.{1,4}$/g); // 下4桁
const winningNumber_3 = String(winningNumber).match(/.{1,3}$/g); // 下3桁
lotteryNumbers.forEach(num => {
const lotteryNumbers_4 = String(lotteryNumbers).match(/.{1,4}$/g);
const lotteryNumbers_3 = String(lotteryNumbers).match(/.{1,3}$/g);
if (winningNumber === num) {
console.log("first");
} else if (winningNumber - num === 1 || winningNumber - num === -1) {
console.log("adjacent");
} else if (winningNumber_4 === lotteryNumbers_4) {
console.log("second");
} else if (winningNumber_3 === lotteryNumbers_3) {
console.log("third");
} else {
console.log("blank");
}
});
});
❌ 失敗ポイント
・正規表現の使い方ミス: match(/.{1,4}$/g)
は配列を返すので、単純比較 (===
) ではうまく動かない。
・誤った変数の使い方: lotteryNumbers
から num
を取得しているのに String(lotteryNumbers).match(/.{1,4}$/g)
を実行してしまっている。
・ロジックが複雑: 正規表現で頑張ろうとしたけど、もっとシンプルな方法がある。
✅ 修正後のコード(OK例)
const rl = require('readline').createInterface({ input: process.stdin });
const lines = [];
rl.on('line', (input) => {
lines.push(input);
});
rl.on('close', () => {
const winningNumber = Number(lines[0]); // 一等の当選番号
const ticketCount = Number(lines[1]); // 宝くじの枚数
const lotteryNumbers = lines.slice(2).map(Number); // 宝くじの番号
lotteryNumbers.forEach(num => {
if (num === winningNumber) {
console.log("first");
} else if (num === winningNumber - 1 || num === winningNumber + 1) {
console.log("adjacent");
} else if (String(winningNumber).slice(-4) === String(num).slice(-4)) {
console.log("second");
} else if (String(winningNumber).slice(-3) === String(num).slice(-3)) {
console.log("third");
} else {
console.log("blank");
}
});
});
✨ 改善ポイント
-
正規表現の使用をやめ、
slice()
を活用
String(winningNumber).slice(-4)
を使えば、下4桁を簡単に取得できる!
match()
の結果が配列になってしまう問題を回避できる。 -
ロジックをシンプルに整理
それぞれの判定を分かりやすくまとめ、ミスが減る。
📝 気づきメモ
match()
は 配列 で返るので、単純な比較には使いにくい。
slice(-4)
を使えば、一発で下桁が取得できて便利!
まとめ
.match()
は配列を返すから相依するのは危険
.slice()
ならシンプルで実装できる
次回、下桁チェックの問題がきたら slice()
を思い出そう
🎁 おまけ: 正規表現で書くなら?
const winningNumber_4 = String(winningNumber).match(/\d{4}$/)[0];
const winningNumber_3 = String(winningNumber).match(/\d{3}$/)[0];
lotteryNumbers.forEach(num => {
const num_4 = String(num).match(/\d{4}$/)[0];
const num_3 = String(num).match(/\d{3}$/)[0];
if (num === winningNumber) {
console.log("first");
} else if (num === winningNumber - 1 || num === winningNumber + 1) {
console.log("adjacent");
} else if (num_4 === winningNumber_4) {
console.log("second");
} else if (num_3 === winningNumber_3) {
console.log("third");
} else {
console.log("blank");
}
});
🚀 改善点
- 正規表現
/\d{4}$/
を使い、下4桁・下3桁を取得 -
match()
の結果は配列なので[0]
で値を取り出す - それ以外のロジックはそのままにして、分かりやすく整理
📌String()
してるのに数字?
確かに String()
を使っているので、数字が文字列に変換されているのに \d
(数字)を使うことに違和感を感じるかもしれないが、実は正規表現では 文字列内の数字を対象にすることができる。
📌なぜ \d
を使っているのか?
正規表現の \d
は、数字の文字(0〜9)を意味しますが、文字列として数字が格納されている場合でも、それにマッチ。たとえば:
const str = "142358"; // 文字列
const result = str.match(/\d{4}$/); // 下4桁を抽出
console.log(result); // ["8358"]
どういうことか?
String()
を使っても、数字は文字列として扱われます。例えば 142358 という数値は “142358” という文字列に変換される。
文字列 “142358” に対して \d を使うと、その文字列の中にある数字を対象にできるので、ちゃんと4桁の数字部分 (8358) がマッチ。
まとめ
String()
で数字を文字列に変換したとしても、正規表現の \d
は文字列内の 数字として表現されている文字 にマッチ。つまり、文字列 “142358” の中にある数字を対象に検索することができる!