見出し画像

【アプリ開発日記46週目】取得した問題のフィルター機能を実装する

 前回、全体の構成が決まりました。あれから「セッションidをuuid4にしようか」などと細かい変更こそあったものの、全体の流れはこのまま行く予定です。

 現時点では

1,メイン画面

2,Bookの作成・保存と表示

3,Sectionの詳細と中の問題一覧

4,urlに対応した問題を表示

といった感じです。今回からはこれらを繋いでいきます!

具体的には

  • セクション画面で問題を選択。設定に応じてシャッフル・フィルター・問題数の調整を行う ← 今回

  • 「演習スタート」を押すと最初の問題に遷移、遷移先で「次へ」を押すと次の問題ページに遷移する

  • 全部解き終えると、正答率を表示

  • オプションで「△×を解き直す」ボタンを作成、その問題だけが入った2周目を始められる

  • 演習中にメイン画面などに戻った場合、「続きから」を選択できる

ということでさっそく実装していきましょう。

1,セクション画面で問題を選択

…といっても書くことがないので少し技術的な内容に触れると、今回はデータベースから問題一覧を取得し、フロントエンド側で

  • まず全問題を整列

続いて、フォームの操作に応じて

  • 選択セクションに応じて問題を抜き出し

  • 選択フェーズ(○、×など)に応じて問題を抜き出し

  • 順番シャッフル

  • 指定した問題数以下になるように調整

の一連の処理を加えてあげれば、ユーザーのほしい問題のみが抽出されてきます。フェーズは演習履歴の登録と取得も必要なため次回行いますが、はじめに「全問題を整列」から行っていきます!

データベースに

問題を登録しておきました。これにgetStaticPropsを使えば……

export async function getStaticProps({ params }) {
  
  const book = await getBookDetail(params.id);
  const sections = await getSectionsByBook(params.id);
  
  let quizzes = [];
  for (let i=0; i < sections.length; i++) {
    quizzes = quizzes.concat(await getQuizzesBySec(sections[i]['id']));
  }

  return {
    props: {
      book, sections, quizzes
    },
    revalidate: 3,
  };
}
localStorageは文字列しか格納できないため、「_」で繋いだ文字列状の配列に変換。後の問題遷移時も、localStorageから前後の問題のidを取得する予定です

無事取得できました! これで全問題取得はクリアですね。続いて「ランダム順」も実装。これはボタンを押すタイミングに上記の配列をシャッフルする処理を作ればよさそうです。

2,シャッフル

const shuffle = ([...array]) => {
  for (let i = array.length - 1; i >= 0; i--) {
    const j = Math.floor(Math.random() * (i + 1));
    [array[i], array[j]] = [array[j], array[i]];
  }
  return array;
}

参考:https://gxy-life.com/2PC/PC/PC20211012.html

結果

順番がバラバラになったのが確認できます!

3,指定セクションの問題のみ抽出

 次に、上記の結果から「選択したセクションの問題のみに絞る」という処理を作っていきます。

 これは一度除外した問題もあとで再選択できるように、全問題を含んだリストもバックアップとして作っておく必要がありそうですね。

 セクション選択前は

ここに

let raw = quiz_all.split('_');

// selectedのvalueを配列で返す
const getValues = name => {
  let inputs = document.getElementsByName(name);
  let array = [];
  for (let target of inputs) {
    if(target.checked) array.push(target.value);
  }
  console.log(array);
  return array;
}

let raw2 = [];
let selected_section_ids = getValues('section');
let selected_phases = getValues('phase');

for (let i = 0; i < raw.length; i++) {
  // 2,セクションに応じて絞る
  if (selected_section_ids.includes(raw[i].split('-')[0])) {
    // 3,フェーズに応じて絞る
    if (!selected_phases.includes(raw[i].split('-')[1])) raw2.push(raw[i]);
    // if (selected_phases.includes(raw[i].split('-')[1])) raw2.push(raw[i]);
  }
}

でチェックされているセクションを取得&問題ごとにforしてセクション番号が一致していれば抽出、という流れでできそうです。

上記の結果

無事セクション2だけを除いた結果が出力されました!

ランダム順もいい感じ。あとは現在すべての問題を引っ張ってきているので、「問題数」に応じて数を絞れば完成です!

4,問題数で抽出

 いよいよ今回最後の項目です、そして楽です!笑

 これも、3と同様、問題数を10で絞った後に20を選択した場合、などの場合を考慮してバックアップをとっておきましょう。あとはfor文で最大値を問題数に設定して回せばいいので、非常にシンプルですね。

 文字列に直しているのでややわかりにくいですが

let count = 10;
if (localStorage.getItem('quiz_count')) count = Number(localStorage.getItem('quiz_count'));
if (raw2.length < count) count = raw2.length;

let final = '';
for (let i = 0; i < count; i++) {
  final += `${raw2[i]}_`;
}

if (final.length > 0) {
  final = final.substring(0, final.length - 1);
} else {
  console.log('問題が選択されていません!');
}

結果

しっかり20個選択されました!

ついでに先頭の問題のidを取得してページ遷移と合体すれば……

問題ページにも遷移できました!

おわりに

 今回は問題のデータを抽出&フィルター機能を実装しました。今まではPython/バックエンド側で実行していた処理ですが、フロントエンドでも意外と同じ処理できるんだな、などといった小さな発見もあります。

 これからパスワード変更処理などを実装する時などはまた苦労しそうですが…だんだんクイズアプリっぽくなってきましたね。次は「△×問題のみ2週目」「続きから」「過去の○×の最新履歴を取得、それに応じてフェーズでフィルターかかるようにする」あたりを実装していきたいと思います。

 ではでは!


この記事が気に入ったらサポートをしてみませんか?