選択と並べ替え

3年データをソート

元のデータは前と同じ 2to3nen.txt です。3年の名簿をソートして出るようにします。

3年の選択ボタンの部分はselect2.phpと同じですが、今回はselect3.phpというページにするのでactionの先も忘れずにselect3.phpに直します。その他は同じ。

<form action="select3.php#koko" method="post" id="koko"
<p>
<input type="submit" name="hr" value="2A">
<input type="submit" name="hr" value="2B">
<input type="submit" name="hr" value="2C">
<input type="submit" name="hr" value="2D">
<input type="submit" name="hr" value="2E">
<input type="submit" name="hr" value="2F">
<input type="submit" name="hr" value="2G">
<input type="submit" name="hr" value="2H">
</p>
<p>
<input type="submit" name="newhr" value="3A">
<input type="submit" name="newhr" value="3B">
<input type="submit" name="newhr" value="3C">
<input type="submit" name="newhr" value="3D">
<input type="submit" name="newhr" value="3E">
<input type="submit" name="newhr" value="3F">
<input type="submit" name="newhr" value="3G">
<input type="submit" name="newhr" value="3H">
</p>
</form>

key,valとksort()を使った方法

PHPプログラムはこちら。

まず、3年の時だけソートが必要なので、newhrに値が入っているかhrに値が入っているかで作業を分けます。 isset($_POST["hr"]) が hr に値が入っているかどうか調べる関数です。

newhrに値が入っているときには、sort.php でやった手法で、key,valを登録しています。今回のkeyはnewnoです。newhrで先に該当するデータだけに絞っているのがif ($newhr === $snewhr){の部分です。newhrで絞っているのでnewnoだけで順番が決まります。

$fp = fopen("2to3nen.txt","r");        
$shr="2Z";
if( isset($_POST["hr"]) ){ 
    $shr =$_POST["hr"];
    while(!feof($fp)){
       $line = fgets($fp);
        if (trim($line) != null){
            list($hr,$no,$sei,$mei,$seib,$ysei,$ymei,$newhr,$newno) = explode("\t",$line);
            if ($hr === $shr){
                print "<tr>\n";
                print "<td>$hr</td><td>$no</td><td>$sei</td><td>$mei</td>";
                print "<td>$seib</td><td>$ysei</td><td>$ymei</td>\n";
                print "<td>$newhr</td><td>$newno</td>\n";
                print "</tr>\n";
            }
        }
    }
}
$snewhr="3Z";
if( isset($_POST["newhr"]) ){
    $snewhr =$_POST["newhr"];
    while(!feof($fp)){
        $line = fgets($fp);
        if (trim($line) != null){
            list($hr,$no,$sei,$mei,$seib,$ysei,$ymei,$newhr,$newno) = explode("\t",$line);
            if ($newhr === $snewhr){
                $key = $newno;
                $val = $line;
                $gotoarray[$key]=$val;
            }
        }
    }
    ksort($gotoarray);
    foreach ($gotoarray as $key => $val) {
        list($hr,$no,$sei,$mei,$seib,$ysei,$ymei,$newhr,$newno) = explode("\t",$val);
        print "<tr>\n";
        print "<td>$hr</td><td>$no</td><td>$sei</td><td>$mei</td>";
        print "<td>$seib</td><td>$ysei</td><td>$ymei</td>\n";
        print "<td>$newhr</td><td>$newno</td>\n";
        print "</tr>\n";
    }
}
fclose($fp);

残念ながら、上記のソートでは次のようになります。

これはksort($gotoarray);の並べ替えがデータを文字列として扱っている為です。

2E2梅村千草うめむらちぐさ 3A1
2E11諏訪亜由香すわあゆか 3A10
2D11相馬美和そうまみわ 3A11
2D12竹浪千秋たけなみちあき 3A12
2A14鳴海麻美なるみあさみ 3A13
2F14野呂春菜のろはるな 3A14
2C16福田初美ふくだはつみ 3A15
2A15蒔苗菜摘まかなえなつみ 3A16
2H16山内志保やまうちしほ 3A17
2C18幸田奈々子ゆきたななこ 3A18
2D19浅利和哉あさりかずや 3A19
2A3岡本おかもとあい 3A2
2D21石川和茂いしかわかずしげ 3A20
2B20一戸いちのへさとし 3A21
.... ..................

これはksortにSORT_NUMERICの指示を加えることで回避できます。ksort($gotoarray)をksort($gotoarray,SORT_NUMERIC)にしてください。

3年のクラスと番号を行の先頭に表示させるのは簡単です。print "<td>$hr</td><td>$no</td>...の部分を変更するだけです。

ただの配列を使う方法

sort.php でksort()を紹介した関係で上記の方法が先になりましたが、今回の条件なら配列に代入するだけでソートを使うことなく実現できます。

c言語やjavaなど「整数の添字で記憶場所を指定する配列」に慣れているとわかりやすい手法です。しかし注意すべき問題もあります。以下3年の部分だけ示します。

$snewhr="3Z";
if( isset($_POST["newhr"]) ){
    $snewhr =$_POST["newhr"];
    while(!feof($fp)){
        $line = fgets($fp);
        if (trim($line) != null){
            list($hr,$no,$sei,$mei,$seib,$ysei,$ymei,$newhr,$newno) = explode("\t",$line);
            if ($newhr === $snewhr){
                $gotoarray[(int)$newno]=$line;
            }
        }
    }
    $count = count($gotoarray);
    for ($i = 1;  $count>=$i; $i++) {
        print "<tr>\n";
        list($hr,$no,$sei,$mei,$seib,$ysei,$ymei,$newhr,$newno)
                       = explode("\t",$gotoarray[$i]);
        print "<td>$hr</td><td>$no</td><td>$sei</td><td>$mei</td>";
        print "<td>$seib</td><td>$ysei</td><td>$ymei</td>\n";
        print "<td>$newhr</td><td>$newno</td>\n";
        print "</tr>\n";
    }
}

(int)はキャストと呼ばれ、$newnoを文字列から整数に変換します。$newnoは$lineから切り取られたので文字列になっています。このままでは$newnoは文字列のままkeyになってしまいます。

forはc言語やjavaなどでよく使われる構文なのでforeachよりよく知られていますが、慣れていない人がforeachより分かりやすいかというと疑問です。

要素数を調べる count($gotoarray)は毎回データ数を数えるのであらかじめ別の変数に覚えさせます。これが添え字の最大値と必ずしも一致しないので問題があります。

たとえば、3Aの2番がデータから抜けていたとします。

2E2梅村千草うめむらちぐさ 3A1
2B5小野寺のぞみおのでらのぞみ 3A3
2G4葛西由佳かさいゆか 3A4
2D5樺沢郁乃かばさわいくの 3A5
.... ..................
2G34福原史学ふくはらふみのり 3A34
2A31藤田健一ふじたけんいち 3A35

3Aの36番があるのですが、2番が抜けて35人分しかデータが無いので35までしか表示しません。2番のデータが無いので2番の所は空欄になっています。空欄になるのは好ましい場合もありますが、入っているデータが表示されないのは厄介です。

phpの配列の基本が「keyとvalueを関係付けるmap」であるため、key,valueを用いてforeachを使うほうがお勧めです。

課題

上の説明は前回の課題2の解答になっています。

前回の課題2 select3.php を完成させなさい。index.htmlに「phpで選択3」という名前のリンクを作りましたか。


ウェブページ(Feb.2012)
聖愛中学高等学校
http://www.seiai.ed.jp/
Feb.2012 初稿