PHP5.2.17で配列に関する質問です。

配列Aを配列Bに作り直したいのです。
配列イメージは補足に記入しました。

1. 配列Aの 'q_ansxx' の4つの部分だけ抜き出す。
(4つは、5つや6つになる可能性があり。'q_ans'の文字列に変更はない。)
2. 単純配列を2次元配列へ作り直し、q_ans01~04の数字の部分を新たに要素を作り代入。
(01~04の型は数字でなく文字列です。)
3. キーを維持したままシャッフル。

上記条件は結果が同じなら、順番が入れ替わっても問題ありません。
コードをご指南ください。
よろしくお願いしますm(_ _)m

回答の条件
  • 1人5回まで
  • 13歳以上
  • 登録:2013/08/27 15:13:37
  • 終了:2013/08/29 22:22:29
id:appfb

質問者から

appfb2013/08/27 16:11:46

配列A

Array
(
	[q_id] => 1
	[q_que] => ほげほげ
	[q_ans01] => あああ
	[q_ans02] => いいい
	[q_ans03] => ううう
	[q_ans04] => おおお
	[q_xxx] => 23
	[q_date] => 2013-08-27 11:39:09
)

配列B

Array
(
	[0] => Array
	(
		[q_key04] => 04
		[q_ans04] => おおお
	)
	[1] => Array
	(
		[q_key02] => 02
		[q_ans02] => いいい
	)
	[2] => Array
	(
		[q_key03] => 03
		[q_ans03] => ううう
	)
	[3] => Array
	(
		[q_key01] => 01
		[q_ans01] => あああ
	)
)

ベストアンサー

id:Sampo No.2

Sampo回答回数556ベストアンサー獲得回数1042013/08/27 23:14:07

ポイント50pt

まず、q_ans01, q_ans02, q_ans03, q_ans04, … というカラムを用意している設計がよろしくありません。

http://www.slideshare.net/t_wada/sql-antipatterns-digest
こちらのスライドの41ページを開いてください。

個数可変の属性にカラムを一個ずつ作ろうというのは、「マルチカラムアトリビュート」という名前のついた悪手(「アンチパターン」)です。

ではどうするか。
選択肢テーブルを別に作るのです(さっきのスライドでは次のページに書いてある「従属テーブル」にあたります)。

テーブル名:Q_choice(pkey Q_id, choice_no)
Q_id(int), choice_no(int), text(varchar) 

Q_choiceからの取り出しは特に迷うことはありませんよね? 取り出してからshuffleしてもいいですし、ORDER BY RAND() をつけてシャッフルしつつ取り出してもかまいません。


さて、コメント欄で予告したUNIONの使い方。もしq_ans01, q_ans02, q_ans03, q_ans04が捨てられず、かつ、SELECT一発で取り出したければ

SELECT 1 AS No, q_ans01 AS Text
FROM Q_master WHERE Q_id={$id}
  UNION
SELECT 2 AS No, q_ans02 AS Text
FROM Q_master WHERE Q_id={$id}
  UNION
SELECT 3 AS No, q_ans03 AS Text
FROM Q_master WHERE Q_id={$id}
  UNION
SELECT 4 AS No, q_ans04 AS Text
FROM Q_master WHERE Q_id={$id}

と問い合わせれば
4行に延ばして取得ができます。

SELECT No, Text FROM
(
  SELECT 1 AS No, q_ans01 AS Text, RAND() AS rank
  FROM Q_master WHERE Q_id={$id}
    UNION
  SELECT 2 AS No, q_ans02 AS Text, RAND() AS rank
  FROM Q_master WHERE Q_id={$id}
    UNION
  SELECT 3 AS No, q_ans03 AS Text, RAND() AS rank
  FROM Q_master WHERE Q_id={$id}
    UNION
  SELECT 4 AS No, q_ans04 AS Text, RAND() AS rank
  FROM Q_master WHERE Q_id={$id}
) ORDER BY rank

とすればランダムシャッフルもできますね。

他1件のコメントを見る
id:Sampo

ご紹介したスライドは「SQLアンチパターン」という本の紹介なのですが、この本はDBを扱うすべての人のマストリードです。
日本語版は今年出たばかりなのですが、これが5年前にあったらあんな苦労しないで済んだのに…という思い出がいろいろと。

ぜひ読んでみてください。
学生の方なら、図書館に購入リクエスト出すのもいいと思いますよ!

2013/08/28 12:17:05
id:appfb

了解しました。
本まで紹介いただきありがとうございます。
経験者のアドバイスはとてもありがたいです。
早速、取り寄せてみます。

2013/08/28 13:10:21

その他の回答(1件)

id:appfb

質問者から

appfb2013/08/27 15:23:54

質問文を編集しました。詳細はこちら

id:a-kuma3 No.1

a-kuma3回答回数4412ベストアンサー獲得回数18032013/08/27 16:13:31

ポイント50pt

クエリでやるっぽいですが、とりあえずコードを。

<?php
$foo = array(
        "q_id"    => "1",
        "q_que"   => "ほげほげ",
        "q_ans01" => "あああ",
        "q_ans02" => "いいい",
        "q_ans03" => "ううう",
        "q_ans04" => "おおお",
        "q_xxx"   => "23",
        "q_date"  => "2013-08-27 11:39:09"
    );

$bar = array();
foreach ($foo as $key => $value) {
    if (preg_match('/^q_ans(.+)/', $key, $match)) {
        array_push($bar, array($match[1], $value));
    }
}


shuffle($bar);

?>

ideone.com で試したのがこちら。
http://ideone.com/WpFk7W

変数のダンプを追加してます。




追記です。

ところで、補足の配列の例が紛らわしいですが、2次元のKEYの部分を'q_key'と'q_ans'に変えることはできますか?

確認不足で、すみません ><。
修正したのが、こちらです。

<?php
$foo = array(
        "q_id"    => "1",
        "q_que"   => "ほげほげ",
        "q_ans01" => "あああ",
        "q_ans02" => "いいい",
        "q_ans03" => "ううう",
        "q_ans04" => "おおお",
        "q_xxx"   => "23",
        "q_date"  => "2013-08-27 11:39:09"
    );
 
$bar = array();
foreach ($foo as $key => $value) {
    if (preg_match('/^q_ans(.+)/', $key, $match)) {
        $n = $match[1];
        array_push($bar, array('q_key' . $n => $n, 'q_ans' . $n => $value));
    }
}
 
 
shuffle($bar);
 
?>

ideone.com で試したのがこちら。
http://ideone.com/eoSV4S

他1件のコメントを見る
id:a-kuma3

ところで、補足の配列の例が紛らわしいですが、2次元のKEYの部分を'q_key'と'q_ans'に変えることはできますか?

回答に追記しました。

2013/08/27 23:05:56
id:appfb

バッチリでございます。
これで、array_pushも覚えました。
ありがとうございます。

2013/08/28 11:21:12
id:Sampo No.2

Sampo回答回数556ベストアンサー獲得回数1042013/08/27 23:14:07ここでベストアンサー

ポイント50pt

まず、q_ans01, q_ans02, q_ans03, q_ans04, … というカラムを用意している設計がよろしくありません。

http://www.slideshare.net/t_wada/sql-antipatterns-digest
こちらのスライドの41ページを開いてください。

個数可変の属性にカラムを一個ずつ作ろうというのは、「マルチカラムアトリビュート」という名前のついた悪手(「アンチパターン」)です。

ではどうするか。
選択肢テーブルを別に作るのです(さっきのスライドでは次のページに書いてある「従属テーブル」にあたります)。

テーブル名:Q_choice(pkey Q_id, choice_no)
Q_id(int), choice_no(int), text(varchar) 

Q_choiceからの取り出しは特に迷うことはありませんよね? 取り出してからshuffleしてもいいですし、ORDER BY RAND() をつけてシャッフルしつつ取り出してもかまいません。


さて、コメント欄で予告したUNIONの使い方。もしq_ans01, q_ans02, q_ans03, q_ans04が捨てられず、かつ、SELECT一発で取り出したければ

SELECT 1 AS No, q_ans01 AS Text
FROM Q_master WHERE Q_id={$id}
  UNION
SELECT 2 AS No, q_ans02 AS Text
FROM Q_master WHERE Q_id={$id}
  UNION
SELECT 3 AS No, q_ans03 AS Text
FROM Q_master WHERE Q_id={$id}
  UNION
SELECT 4 AS No, q_ans04 AS Text
FROM Q_master WHERE Q_id={$id}

と問い合わせれば
4行に延ばして取得ができます。

SELECT No, Text FROM
(
  SELECT 1 AS No, q_ans01 AS Text, RAND() AS rank
  FROM Q_master WHERE Q_id={$id}
    UNION
  SELECT 2 AS No, q_ans02 AS Text, RAND() AS rank
  FROM Q_master WHERE Q_id={$id}
    UNION
  SELECT 3 AS No, q_ans03 AS Text, RAND() AS rank
  FROM Q_master WHERE Q_id={$id}
    UNION
  SELECT 4 AS No, q_ans04 AS Text, RAND() AS rank
  FROM Q_master WHERE Q_id={$id}
) ORDER BY rank

とすればランダムシャッフルもできますね。

他1件のコメントを見る
id:Sampo

ご紹介したスライドは「SQLアンチパターン」という本の紹介なのですが、この本はDBを扱うすべての人のマストリードです。
日本語版は今年出たばかりなのですが、これが5年前にあったらあんな苦労しないで済んだのに…という思い出がいろいろと。

ぜひ読んでみてください。
学生の方なら、図書館に購入リクエスト出すのもいいと思いますよ!

2013/08/28 12:17:05
id:appfb

了解しました。
本まで紹介いただきありがとうございます。
経験者のアドバイスはとてもありがたいです。
早速、取り寄せてみます。

2013/08/28 13:10:21
  • id:Sampo
    もしかして、クイズマスタテーブルにはq_ans01, q_ans02, q_ans03, q_ans04 というカラムがあるということでしょうか?

    SQLクエリでちょっとUNIONを使えば、最初から配列Bの形で、シャッフルもしてDBから取得することも可能ですよ。
  • id:appfb
    マジデスカ!
    先日に引き続きありがとうございます。
    はいご推察の通りクイズマスタにカラムがございます。

    私には難易度が高すぎてさっぱり見当が付きません。
    どのようにすればいいでしょうか?
  • id:Sampo
    確認ですが、q_ans05や06もあるんですよね。

    4択問題であることはどうやってわかるようになっていますか?
  • id:windofjuly
    うぃんど 2013/08/27 16:28:06
    結局UNIONでやるの?phpでやるの?

    UNIONでやるならphp側もコールバックかループ使うことになるけど、
    DBにカラムがあるなら、決め打ちで済んで楽じゃないのかな?

    $配列A = $result->fetch_assoc();
    $配列B = array();
    $配列B[] = array( 'q_key01'=> '01', 'q_ans01' => $配列A["q_ans01"] );
    $配列B[] = array( 'q_key02'=> '02', 'q_ans02' => $配列A["q_ans02"] );
    $配列B[] = array( 'q_key03'=> '03', 'q_ans03' => $配列A["q_ans03"] );
    $配列B[] = array( 'q_key04'=> '04', 'q_ans04' => $配列A["q_ans04"] );
    shuffle( $配列B );
  • id:appfb
    現在5択なのですが、SQLクエリでできるなんて考えもしませんでしたから、先日の条件でとりあえず一旦取り出してから考えようと思っていました。
    今後選択肢(カラム)を増やすかもしれないと考え以上のような質問となりました。
    また、クイズ難易度に関しても選択肢を増減させようとも思っています。
    例えば、MySQLのカラムには5択用意してありますが、初心者向けのクイズには3択、クイズベテランには5択など。

    もっと知識があれば、いくらでも効率の良いプログラミングはあるのでしょうが・・・。
    まずは今回、PHPで行ったほうが身の丈に合ってるような気がします。

この質問への反応(ブックマークコメント)

「あの人に答えてほしい」「この質問はあの人が答えられそう」というときに、回答リクエストを送ってみてましょう。

これ以上回答リクエストを送信することはできません。制限について

絞り込み :
はてなココの「ともだち」を表示します。
回答リクエストを送信したユーザーはいません