Javascriptで受け取った4桁の数字のチェックを行いたいのですがどのようにすればよいかわかりません。
チェックする内容は続き数字が3つ以上連続であるとエラーを返したいのです。
OK: 8236 <-- "23"の部分は連続しているが2つなのでOK
OK: 2378 <-- "23", "78"の部分は連続しているが2つなのでOK
ERROR: 2348 <-- "234"の部分が3つ連続している
ERROR: 2678 <-- "678"の部分が3つ連続している
ERROR: 4567 <-- "4567"の部分が4つ連続している
前提条件としては数字の中には同じ数字は含まれません。
考え方としては受け取った数字を配列に変換してループで回して・・・???そもそもこの考え方自体が正しいのかもわからず考え方の方向性も思いつかない状況です。
何か良いアイデアがあればご教授頂けると幸いです。よろしくお願いします。
javascript
1function checkNum(num){ 2 var arr = num.toString().split('') 3 var isValid = true 4 5 for (var i in arr) { 6 // チェックロジックでNGならisValid = false 7 } 8 9 return isValid 10} 11 12checkNum(2346) // false 13checkNum(2378) // true
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答5件
0
こんにちは。
この回答の前提として、ご質問にある
続き数字
を、1ずつ増えていく、0以上9以下の整数を項とする数列
と解釈しています。以下の回答の文中に
出てくる "連続する数字
" も同様に、1ずつ増える、2個以上の整数を意味します。
上記を前提として、以下のような checkNum
の実装を考えました。
javascript
1/* 2 * checkNum(n) 3 * 4 * 引数 n : 4桁の整数 5 * 6 * 返される値 7 * true : n の中に連続する3個以上の数字が含まれない 8 * false : n の中に連続する3個以上の数字を含む 9 */ 10const checkNum = n => [Math.floor(n / 10), n % 1000].every(x => (x - 12) % 111); 11 12// テスト 13[ 14 8236, 15 2378, 16 2348, 17 2678, 18 4567 19].forEach(e => { 20 console.log(`${checkNum(e) ? 'OK' : 'ERROR'}: ${e}`); 21});
(※上記のコードを、https://codepen.io/jun68ykt/pen/gOpdMPQ?editors=0012 にも上げました。)
上記を実行すると、以下が表示されます。
OK: 8236
OK: 2378
ERROR: 2348
ERROR: 2678
ERROR: 4567
考え方としては、
3桁の整数 x について、x が連続する数字3つで構成されるならば、( x - 12 ) は 111 で割り切れる
( 例: x = 567 とすると、(567 - 12) ÷ 111 = 555 ÷ 111 = 5 余り 0 )
ことを利用しています。具体的には 4桁の数 n
から、以下の二つの数(ともに3桁)
- 先頭3文字の数:
Math.floor(n / 10)
- 末尾3文字の数:
n % 1000
を作り、これらの
- 両方ともに、12引いてから 111で割ると割り切れないならば true を
- いずれかが、 12引いてから 111で割ると割り切れるならば false を
返すように、checkNum(n)
を作成しました。
以上、参考になれば幸いです。
補足
900 から 12を引くと 888で、888 は 111で割り切れます。
従って、たとえば 9001 は連続した3つの数を含んでいなくても、
上記回答の checkNum(9001)
は false
を返しますが、
ご質問の中に
前提条件としては数字の中には同じ数字は含まれません。
とあるので、 9001 のほか、 9008 や 2900 などは0 を2つ含むので、
チェック対象の数(checkNum(n)
の引数 n
)としては考慮していません。
投稿2018/06/20 18:13
編集2020/03/22 00:15総合スコア9058
0
ベストアンサー
続き数字が3以上と言うことなので。(987とかは入れてないけど)
js
1function checkNum( num ) { 2 return ! /012|123|234|345|456|567|678|789/.test( num ); 3} 4 5console.log( checkNum( 2346 ) ); // false 6console.log( checkNum( 2378 ) ); // true
Array.prototype.reduce() でも書いてみた。
js
1function checkNum2( num ) { 2 const arr = num.toString().split( '' ); 3 let tmp = 0; 4 let count = 0; 5 arr.reduce( ( pre, curr )=> { 6 if ( Math.abs( pre - curr ) === 1 ) { 7 tmp++; 8 } else { 9 if ( tmp >= 2 ) count++; 10 tmp = 0; 11 } 12 return curr; 13 } ); 14 if ( tmp >= 2 ) count++; 15 return count === 0; 16} 17 18console.log( checkNum2( 2346 ) ); // false 19console.log( checkNum2( 2378 ) ); // true
reduce 使わないほうが良いかな。
js
1function checkNum3( num ) { 2 const arr = num.toString().split( '' ); 3 const l = arr.length 4 if ( l < 3 ) return true; 5 let prev = arr[ 0 ]; 6 let tmp = 0; 7 for ( let i = 1; i < l; i++ ) { 8 if ( Math.abs( prev - arr[ i ] ) === 1 ) { 9 tmp++; 10 } else { 11 if ( tmp >= 2 ) return false; 12 tmp = 0; 13 } 14 prev = arr[ i ]; 15 } 16 return true; 17} 18 19console.log( checkNum3( 2346 ) ); // false 20console.log( checkNum3( 2378 ) ); // true
ちょっと改良。(ちなみに「同じ数字が出ない」仕様でなくなった場合は Math.abs で処理している部分の符号をチェックする必要がある)
js
1function checkNum4( num ) { 2 const arr = num.toString().split( '' ); 3 const l = arr.length - 2; 4 if ( l < 1 ) return true; 5 for ( let i = 0; i < l; i++ ) { 6 if ( ( Math.abs( arr[ i ] - arr[ i + 1 ] ) === 1 ) && ( Math.abs( arr[ i + 1 ] - arr[ i + 2 ] ) === 1 ) ) return false; 7 } 8 return true; 9} 10 11console.log( checkNum4( 2346 ) ); // false 12console.log( checkNum4( 2378 ) ); // true 13```**動くサンプル:**[https://jsfiddle.net/q7eyp0uh/4/](https://jsfiddle.net/q7eyp0uh/4/)
投稿2018/06/20 14:10
編集2018/06/20 15:44総合スコア69398
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
続き文字の定義はこれで合ってますか?
- 不正に該当するのは1個ずつ上昇する数値だけか?(例:111や321や121は全て妥当)
- 9の次は0になるのか?(例:901は不正)
ひとまず両方正として回答していきます。
偽になる場合は適宜読み替えてください。
考え方としては受け取った数字を配列に変換してループで回して・・・???そもそもこの考え方自体が正しいのかもわからず考え方の方向性も思いつかない状況です。
3文字特定の連番が続いたらアウトなのですよね?
「連番が続いている数」という変数を定義して、for文でループされる度に増やしていくという手法が良いでしょうね。
1文字ずつ判定して、前回の文字列と比較してけば良いでしょう。
アウトになった瞬間return false
を返せばループ内だとしても即刻関数を終了させて戻り値を返しますので、
今回のケースではisValid
変数は不要です。
JavaScript
1function checkNum(num){ 2 var arr = num.toString().split('') 3 var last = null; 4 var count = 0; 5 6 for (var i in arr) { 7 var it = arr[i]; 8 if (!last) { 9 // 最初の文字を取得したら連番1とみなす 10 count = 1; 11 } else if (parseInt(it) === parseInt(last) + 1) { 12 // 取得文字が前回の文字より1多い場合、連番カウントを加算 13 count++; 14 } else if (last === '9' && it === '0') { 15 // 取得文字0、前回の文字9の場合は連番とみなしてカウントを加算 16 count++; 17 } else { 18 // 連番じゃなかったんですね、連番カウントは1になる 19 count = 1; 20 } 21 last = it; 22 23 // 連番が3個続いたらfalseを返すのでしたよね 24 if (count >= 3) { 25 return false; 26 } 27 } 28 29 // アウトになる連番が出なかったので妥当ということでtrueを返す 30 return true; 31} 32 33checkNum(1245) 34// true 35checkNum(1235) 36// false
質問文から素直にコードにするとこんな感じのところがゴールになります。
もう少し発展させてエレガントなコードにするとkeiさんのコードになるでしょう。
おまけ: コードを洗練させていく
引き出しのネタとして全く違うアプローチから解いていきます。
JavaScript中級者向けの内容となっていますので、分かる部分だけ持っていってください。
AとBは連番であるというところを抜き出しましょうか
シリアルナンバーと言えばなんか別のモノを指すように思えますが、まぁいいや、isシリアルナンバーという関数を用意します。
JavaScript
1function isSerialNumber (a, b) { 2 if (a === 9 && b === 0) return true; 3 if (b === a + 1) return true; 4 return false; 5} 6isSerialNumber(1, 2) // true 7isSerialNumber(9, 0) // true 8isSerialNumber(1, 3) // false
続いて、1234
という数字列の判定をしていきます。
これは左の数字と、右の数字が連結…つまり1と2
、2と3``3と4
文字目の連結を確認して、
2つの連結が同時に起こっている場合は3文字連番が続いているので不正と判断出来ます。
従って下記のような順序でデータを加工できれば判別が可能となります。
["1", "2", "3", "4"]
[[1, 2], [2, 3], [3, 4]]
[true, true, true]
3
← trueが連続して続いたMax回数return 3 < 2
← 2回以上続いたらfalseを返す
JavaScriptの全ての値はプロトタイプというメソッドがひっついています。
質問文のnum.toString().split('')
はまさにそれで、JSの全ての型にはStringに変換するtoString
メソッドが用意されており、文字列に変換した後にsplit
メソッドを利用して配列型に変換しています。
さらに配列の型には、mapやreduceといったメソッドが用意されており、配列の各要素を好きなように加工する事ができます。
mapやreduceはメソッドは関数を引数として要求するので、何度も関数定義を書くことになり行数が増えがちなのですが、
ES2015というバージョンでアロー関数という書き方が使える様になりました。
モダンブラウザではIE11以外のほぼ全てのブラウザで利用可能です。
早速上記の連番判定関数をアロー関数で書き直してみます。
(functionが消えて=>
という2文字の矢印を使ってるだけです、1行で値を返す場合は{}
を省略出来たりと便利)
JavaScript
1var isSerialNumber = (a, b) => { 2 if (a === 9 && b === 0) return true; 3 if (b === a + 1) return true; 4 return false; 5} 6isSerialNumber(1, 2) // true 7isSerialNumber(9, 0) // true 8isSerialNumber(1, 3) // false
では、早速データを加工していきましょう。
今回は連番している状態を1、連番ではない状態を0として文字列変換を経由する事で実現させます。
詳細は各行の末尾にどんな値に変換されるかをコメントで記載しました。
JavaScript
1var isSerialNumber = (a, b) => { 2 if (a === 9 && b === 0) return true; 3 if (b === a + 1) return true; 4 return false; 5} 6function checkNum(num){ 7 var arr = num.toString().split('') // ["1", "2", "3", "4"] 8 var maxLength = arr 9 .map((it, i) => i === 0 ? null : [parseInt(arr[i - 1]), parseInt(it)]) // [null, [1, 2], [2, 3], [3, 4]] 10 .slice(1) // [[1, 2], [2, 3], [3, 4]] 11 .map(it => isSerialNumber(it[0], it[1])) // [true, true, true] 12 .map(it => it ? "1" : "0") // ["1", "1", "1"] 13 .join('') // "111" 14 .split('0') // ["111"] 15 .map(it => it.length) // [3] 16 .reduce((a, b) => Math.max(a, b), 0) // 3 17 return maxLength < 2; 18} 19checkNum(1245) // true 20checkNum(1235) // false
そこそこ難解なので興味が出てきたらという感じで学習してみてください。
メソッドチェインを覚えるとこんな感じで値を転がしていけるようになるので慣れたら楽です。
最初はとっつきにくいと思いますので、1行ずつ実行していってみてください。
投稿2018/06/20 15:15
編集2018/06/20 16:23総合スコア21158
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2018/06/20 15:17
2018/06/21 10:15
2018/06/21 20:01
2018/06/22 03:34 編集
0
要件
- 「4桁の数値」と「初項 0、交差 1、項数 10 の等差数列」を照合する
- 等差数列は 901 のように繰り上げされない
- 等差数列は 987 のように交差 -1 にはならない
- 「4桁の数値」から「前方3桁」「後方3桁」の数値を切り出し、等差数列と照合して一致したら
false
、不一致ならtrue
を返す
コード
JavaScript
1'use strict'; 2function checkNum (number) { 3 const progression = '0123456789', 4 numberString = String(number).slice(0,4); 5 6 return !progression.includes(numberString.slice(0,3)) && !progression.includes(numberString.slice(1)); 7} 8 9console.log(checkNum(8236)); // true 10console.log(checkNum(2378)); // true 11console.log(checkNum(2348)); // false 12console.log(checkNum(2678)); // false 13console.log(checkNum(4567)); // false
要件を変えるには
数列を書き換えれば、多少の要件変更には対応できます。
901 の繰り上げを許すなら、
JavaScript
1const progression = '012345678901';
交差 -1 の等差数列を許すなら、
JavaScript
1const progression = '0123456789876543210';
Re: hoehoe さん
投稿2018/06/20 20:57
編集2018/06/20 20:59総合スコア18162
0
reduce使ったことなかったので勉強になりました。 > kei344さん
reduceで1桁づつ差を取った結果を文字列にして
matchで連続数を判定します。
hoehoeさんの意図とは若干外れますが…
以下のサンプルだと、差が1or2が2から3桁続くと その桁を返します。
一致しなけばNULLかな。
js
1let num = 16403234; 2 3var arr = num.toString().split(''); 4var dif = ""; 5 6arr.reduce((pre, curr) => { 7 console.log(`${pre} - ${curr}`); 8 dif += Math.abs(pre - curr); 9 return curr; //次回のreduceのpreとする。returnしないと、pre=undefind 10 }); 11 12console.log(arr); 13console.log(dif); 14console.log( dif.match(/[12]{2,3}/).index);
投稿2018/06/20 16:13
総合スコア2826
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2018/06/21 10:14