JS|別ページのデータから条件に従いクイズを選び出す関数|2次元配列制御方法

勘違いしながらドはまりしながら作り上げていく『JavaScript奮闘記』。事始めのJavaScript常識をまとめたページや現在作ろうとしてるクイズの仕様説明はこちら。

ポク太郎です。

本ページの話題は「JavaScriptで別ページ内の情報を取得後、条件に合うデータを選び出す。」『JavaScript3択クイズを作ろう』中の一機能。

別ページから読み込んだクイズ問題の中から実際に出題する問題を選び出すルーチン。ほとんどは2次元配列の使い方の話。


前回、別ページのデータを読み込んだ

別ページからデータを読み込むルーチンはこちら。詳細はこちら

その問題のデータが書いてある別ページのURLはhttps://poku.blog/js-test

document.getElementById("debug").innerHTML = '';
var obj = new XMLHttpRequest();
obj.open('GET','https://poku.blog/js-test',true);//true:非同期通信
obj.onreadystatechange = function(){

	if (obj.readyState === 4 && obj.status === 200){
		var str = obj.responseText; //読み込んだHTMLを変数に代入
//----処理
		var qdt = str.match(/<div class="qdt">.+?<\/div>/g);

			document.getElementById("debug").style.display = '';
		//id="debug"タグにテキストを記述する。
		for (var i=0;i<qdt.length;i++){
			document.getElementById("debug").innerHTML += qdt[i];
		}
//----処理
	}

	};
	obj.send(null); //リクエストの送信

上記の結果、qdt[i]には以下のデータが代入されました。

9000,足し算,r,3+5=?,8○,9,10,正解は「8」です。
9001,足し算,r,4+5=?,8,9○,10,正解は「9」です。
9002,足し算,r,3+9=?,12○,15,10,正解は「12」です。
9003,足し算,r,2+8=?,8,9,10○,正解は「10」です。
9004,引き算,r,2-5=?,-3○,-5,7,正解は「-3」です。
9005,引き算,r,11-4=?,7○,9,10,正解は「7」です。
9006,引き算,r,9-2=?,7○,6,11,正解は「7」です。
9007,引き算,r,8-5=?,3○,2,13,正解は「3」です。
9008,掛け算,r,2×5=?,10○,12,7,正解は「10」です。
9009,掛け算,r,11×4=?,44○,42,7,正解は「44」です。
9010,掛け算,r,4×4=?,16○,20,8,正解は「16」です。
9011,掛け算,r,3×4=?,12○,10,7,正解は「12」です。
9012,割り算,r,10/5=?,2○,3,4,正解は「2」です。
9013,割り算,r,9/3=?,3○,4,5,正解は「3」です。
9014,割り算,r,3/2=?,1.5○,2,1,正解は「1.5」です。
9015,割り算,r,4/2=?,2○,1,4,正解は「2」です。
9016,慣用句,r,○○を漢字で埋めなさい。豚に○○。,真珠○,念仏,小判,正解は「真珠」です。
9017,慣用句,r,○○を漢字で埋めなさい。人生万事塞翁が○,馬○,豚,猿,正解は「馬」です。
9018,慣用句,r,○○を漢字で埋めなさい。猿吉は○でも踏んどれ。,麦○,米,肉,正解は「麦」です。
9019,慣用句,r,○○を漢字で埋めなさい。パンツトラナイデネ!○○トコナイデネ!,二度○,再度,既,正解は「二度」です。

データのフォーマットは以下としました。

クイズNo,カテゴリ,シャッフルフラグ,問題文,選択肢1,選択肢2,選択肢3,正解の解説文

としました。シャッフルフラグとは選択肢の順番を並び替えて出題するかどうか、また選択肢中の正解には文末に○。

条件に従いクイズを選び出す関数

プログラムの仕様として、別ページBからクイズデータ全体を読み取り、そこから出題数分だけ出題データ配列qx[][]に入れてしまう構成としました。

つまり、qx[0,1,2,…][]の1次元の要素にクイズ番号、2次元の要素にqx[n][クイズNo,カテゴリ,シャッフルフラグ,問題文,選択肢1,選択肢2、…]を代入。

だから例えば、qx[2][3]はクイズNo.9002の問題文が、qx[2][5]はクイズNo.9002の選択肢2が代入されてる配列変数。

データフォーマットにシャッフルフラグを定義しましたが、選択肢をシャッフルした結果も反映させて配列qx[][]に代入します。

出題分そのままを配列qx[][]にしておけば、順番に表示するだけで目的が果たせるし、最後に表示するクイズの正誤解説文作成時にも配列qx[][]だけ意識すればよい構造となるので。

つまり、別ページBのクイズデータベースの全内容qdt[]を、条件に従い選び出した出題データqx[][]として変形コピーしてしまう方法。

定義した変数

定義した変数
qdt[]別ページから読み込んだ全クイズデータ。
qdt2[][]2次元配列化した全クイズデータ。
mode_tudo(ここでは未使用)1問回答ごとに正誤結果を表示するかどうかの設定。
catego選び出すカテゴリ設定。
ntest出題数設定。
count(ここでは未使用)問題番号。
q_sel(ここでは未使用)選択肢の数。
qx[][]出題決定した問題の2次元配列。
an[](ここでは未使用)正解だったかどうか。

定義したグローバル関数

定義したグローバル関数
quiz_make()読み込んだクイズデータから条件に従い実際に出題する質問を選び出す関数。
rand(min,max)乱数発生関数。最小値と最大値を引数として設定する仕様。
quiz_shuffle()クイズデータに選択肢の順番を入れ替えるためのシャッフルフラグを持たせた仕様。そのシャッフル機能を実現する関数。

クイズを選び出すJavaScriptソースコード

var qdt=[];//クイズのデータ
var qdt2=[];//クイズのデータ2次元配列化

var mode_tudo=1;//1:1問づつ正誤表示
var catego='掛け算';

var ntest=3;//出題数
var count=0;//現在の問題番号
var q_sel=3;//選択肢の数

var qx=[];//qx[]出題された問題
var an=[];//an[]正解したら1違うなら0

document.getElementById("debug").innerHTML = '';
var obj = new XMLHttpRequest();
obj.open('GET','https://poku.blog/js-test',true);//true:非同期通信
obj.onreadystatechange = function(){

	if (obj.readyState === 4 && obj.status === 200){
		var str = obj.responseText; //読み込んだHTMLを変数に代入
//----処理
		qdt = str.match(/<div class="qdt">.+?<\/div>/g);

			document.getElementById("debug").style.display = '';

		quiz_make();

//----処理
	}

	};
	obj.send(null); //リクエストの送信

function rand( min, max ) {
//範囲選択可能な乱数
	var random = Math.floor( Math.random() * (max + 1 - min) ) + min;
	return random;
}

function quiz_make() {
//クイズデータの作成
	var tmp=[];//2次元配列作成用
	var sel=[];//既に選択済の番号記憶

	//2次元配列qdt2にデータ入れ直し
	for (var i=0;i<qdt.length;i++){
			tmp=qdt[i].split(',');
			qdt2[i]=[];//JavaScript常識、配列の階層ごとに初期化要
		for (var j=0;j<tmp.length;j++){
			qdt2[i][j]=tmp[j];
		}
	}

							var n;
							var find;

	//無限ループに陥らないよう条件に合うcategoの数>出題数を先に確認する
							var kazu = 0;
							var jd;
	for (var i=0;i<qdt.length;i++){
			if (qdt2[i][1] == catego){
							kazu = kazu + 1;
			}
	}
	if (kazu >= ntest){
							jd = true;
	} else {
							jd = false;
	}

	if (jd == false){
				document.getElementById("debug").innerHTML += kazu + 'エラー:条件に合うクイズデータが出題数より少なく無限ループに陥ります。';
	} else {
		for(i=0;i<ntest;i++){
			while(0==0){
							find=1;
							n=9000+rand(0,qdt.length-1);
	//	document.getElementById("debug").innerHTML += 'n=' + n;
				if (qdt2[n-9000][1] == catego){
							find=0;
					for (i=0;i<sel.length;i++){
						if (sel[i] == n){
							find=1;
							break;
						}
					}
				}
				if(find == 0){
							sel.push(n);
							break;
				}
			}
		}
	}

				tmp=[];//2次元配列作成用
	for (i=0;i<sel.length;i++){
				n=Number(sel[i]-9000);
				tmp=qdt[n].split(',');
				qx[i]=[];//JavaScript常識、配列の階層ごとに初期化要
		for (j=0;j<tmp.length;j++){
				qx[i][j]=tmp[j];
		}
	}
				quiz_shuffle();
				count = 1;

	//id="debug"タグに選び出したクイズデータqxを記述する。
	for (i=0;i<qx.length;i++){
	//	document.getElementById("debug").innerHTML += '選択したクイズNo.:' + qx[i][0] + ' カテゴリ:' + qx[i][1] + ' 問題文:' + qx[i][3] + '<br />';
	}
}

function quiz_shuffle() {
//シャッフルフラグの対応
	//クイズのフォーマット
	//クイズNo,カテゴリ,シャッフルフラグ,問題文,選択肢1,選択肢2,選択肢3,正解の解説文

						var tmp;
						var a,b;
	for (var i=0;i<qx.length;i++) {
		if(qx[i][2] == 'r'){
			for (var j=0;j<100;j++) {
			//100回入替を行う
						a = rand(4,4+q_sel-1);
						b = rand(4,4+q_sel-1);
						tmp = qx[i][a];
						qx[i][a] = qx[i][b];
						qx[i][b] = tmp;
			}
		}
	}

	//id="debug"タグに選び出したクイズデータqxを記述する。
	for (i=0;i<qx.length;i++){
		document.getElementById("debug").innerHTML += '選択したクイズNo.:' + qx[i][0] + ' 選択肢1:' + qx[i][4] + ' 選択肢2:' + qx[i][5] + ' 選択肢3:' + qx[i][6] + '<br />';
	}
}

ソースコード説明

実際に出題するクイズを選び出す関数quiz_make()

実際に出題するクイズを選ぶために、別ページBから読み込んだqdt[]を2次元配列化しqdt2[][]へ。

が、ここで注意点→JavaScriptで多次元配列を使用する際は配列の階層ごとに初期化が必要だそう。

なので、.split(‘,’)tmp[]に代入した,区切りのデータをqdt2[][]に代入する際、qdt2[i]=[];と2次元目の階層を初期化しております。

qdt2=[];と書くと1次元配列qdt2の初期化。なので、2次元目の初期化は1次元部分に対してこう→qdt2[i]=[];

2次元配列qdt2[][]を完成させた後、実際に出題する問題をランダムに選び出す部分がその次。

乱数で選択→選択済の番号を保持し同じ問題が選ばれないようにします。既に出題決定したクイズNo.を保持するのが配列変数sel[]。2次元配列qdt2[][]はこれを作るためだけに準備しました。

乱数で異なるクイズNo.が生成されるまでwhile文でループ。ただし、無限ループに陥らないよう、条件「該当カテゴリのデータ数kazuntest」を満たすことを事前に数えて確認しています。

次は、配列sel[]に従い別の2次元配列qx[][]に該当データを同様の方法でコピー。

その後、選択肢をシャッフルさせる関数に移行。シャッフルフラグ付きなら関数rand()を利用して選択肢記憶部分=配列qx[][]の要素4、5、6番目の入れ替えを100回繰り返しています。

これで実際に出題する問題qx[][]が完成。qx[0][]qx[1][]qx[2][]、…、とクイズを出題表示するだけでよい配列変数が出来上がりました。

別ページの情報を読み、条件に従い選び出した結果

ここから

ここまで表示領域。

本ページでは、別ページBからのデータから条件に従い実際に出題する問題を選び出すまでを実施してみました。

JavaScript3択クイズを作ろう
Step1
仕様・必要機能
Step2
別ページのHTML取得
Step3
出題用2次元配列へ
Step4
閲覧者の回答検出・判定

コメント

タイトルとURLをコピーしました