質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Q&A

解決済

5回答

7068閲覧

Javascriptで数字のチェックを行いたい

hoehoe

総合スコア7

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

0グッド

1クリップ

投稿2018/06/20 13:54

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ページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答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
jun68ykt

総合スコア9058

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hoehoe

2018/06/21 10:14

ご回答有り難うございます。 「3桁の整数 x について、x が連続する数字3つで構成されるならば、( x - 12 ) は 111 で割り切れる」 すごい!数学的に成り立っている!とても参考になりました。
guest

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
kei344

総合スコア69398

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hoehoe

2018/06/21 10:15

ご回答有り難うございます。なんと!今回の条件だと8種類のパターンのチェックだけでよかったのですね、もっと複雑な考え方でゴールを目指していました。たった一行の正規表現で完結できてしまうとは本当に目からウロコでした。checkNum4の考え方は(かなり漠然とですが)思い描いていたロジックに近かったので今後これを自分で書けるようにがんばって行きたいです。 reduceは今回は遠慮させていただきます。 一番最初に回答を頂き、また4種類もの回答例を頂いたのでこちらをベストアンサーに選ばせていただきました。
guest

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と22と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メソッドを利用して配列型に変換しています。

さらに配列の型には、mapreduceといったメソッドが用意されており、配列の各要素を好きなように加工する事ができます。

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
miyabi-sun

総合スコア21158

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

miyabi-sun

2018/06/20 15:17

因みに`"0" ? true : false`を評価するとtrueになります。 なので最初の判定を単に`!last`でよしとしています。
hoehoe

2018/06/21 10:15

ご回答有り難うございます。配列の中身を2つずつに分けて考えるのは興味深いアイデアですね。メソッドチェーンも覚えると「値を転がしていける」というのはとても便利だと思いました。 0 ? true : false // false "0" ? true : false // true これも興味深いですね。(文字列を三項演算子に入れるとtrueを返す、であっているのかな?)
miyabi-sun

2018/06/21 20:01

三項演算子やIF文はBoolean型をよこせと言ってきます。 しかし、0や"0"はBoolean型ではないので型変換を行って評価します。 JSの型変換でBoolean型へ変換されるときの仕様はECMAScriptで決まっています。 例えばNumber型は0がfalse、1以上でtrueです。 String型は空文字がfalse、何か入っていれば例え"0"や"false"というfalseになりそうな値もtrue扱いになります。
think49

2018/06/22 03:34 編集

> 例えばNumber型は0がfalse、1以上でtrueです。 仕様上は「引数が+0, -0, もしくは NaN なら false を返し、それ以外なら true を返す」ですね。 つまり、引数が-1や0.1はなら true を返します。 http://www.ecma-international.org/ecma-262/8.0/#sec-toboolean (自然数を前提としているのかもしれませんが、誤解が生まれるといけないので、念の為)
guest

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
think49

総合スコア18162

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hoehoe

2018/06/21 10:14

ご回答有り難うございます。 ES6のstring.includes()でも簡単に結果が導けるのですね、とても参考になりました。progression の書き換えで「901 の繰り上げ」や「交差 -1 の等差数列」にも対応できるという考え方は今後の参考にさせていただきます。ありがとうございました。
guest

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

oikashinoa

総合スコア2826

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問