PHPの配列とリスト

配列の生成

PHPの配列はarray()で生成しますが、始めから キー => 値 の組として存在します。

いわゆる値だけを並べたものは、数値の添字を省略したものと考えます。

まず、javascriptに合わせて、省略されたものから扱います。

さらにPHP 5.4 以降にarray()の代わりに[]を使えるようにもなっています。

結果としてjavascriptに近いものになっていますが、スタートが違うような気がします。

$names = array("mike","tama","kuro");
$names = ["mike","tama","kuro"];

特筆すべきは、最後のコンマが無視されることです。これで key=>value, を行ごとに書く時になど、要素の追加が楽になります。

最初のプログラムはこれです。あえてforを使わずに。

<?php
$names = array("mike","tama","kuro");
echo "<table><tr>";
echo "<td>",$names[0],"</td>";
echo "<td>$names[1]</td>";
echo "<td>$names[2]</td>";
echo "</tr><tr>";
echo "<td>".count($names)."</td>";
echo '<td colspan="2">count($names)</td>';
echo "</tr></table>";

実行結果です。$names[0]と$names[1]のところの書き方は両方ありなので、あえて書き換えています。

miketamakuro
3count($names)

ちなみにechoなど

echoはコンマで区切られた複数の引数を出力します。( )は不要です。返値はなしです。

コンマをドットにすると文字列が連結されて出力されます。見た目は同じです。

""内の変数は変数の内容と置き換えられますので、コンマやドットを使わずにスマートに書くことが可能です。

''を使うと変数を展開しません。

printも使えますが、引数はひとつだけなので、コンマで並べることはできません。引数に( )は不要です。返値は常に 1 ですが受ける必要はありません。

printf()はフォーマット済みの文字列を出力する関数です。

sprintf()はフォーマットされた文字列を返します。

$num = 12.3;
echo $names[0],$names[1],$num,"\n";
print $names[0].$names[1].$num."\n";
print "$names[0] $names[1] $num\n";
printf("%5.2f\n",$num);
echo sprintf("%5.2f\n",$num);
miketama12.3
miketama12.3
mike tama 12.3
12.30
12.30

var_dumpなど

配列などの内容の確認に便利に使えます。keyを省略すると数値が補われることがわかります。

var_dumpではvalueのtypeがわかります。

var_exportではプログラムに書き戻せる形で表示されます。

print_r($names);
var_dump($names);
var_export($names);
Array
(
    [0] => mike
    [1] => tama
    [2] => kuro
)
array(3) {
  [0]=>
  string(4) "mike"
  [1]=>
  string(4) "tama"
  [2]=>
  string(4) "kuro"
}
array (
  0 => 'mike',
  1 => 'tama',
  2 => 'kuro',
)

要素の種類の混在

要素の種類がいろいろ混在しても構いません。

$iroiros = ["mike",365,3.14,true,$names];
echo "<table><tr>";
echo "<td>",$iroiros[0],"</td>";
echo "<td>",$iroiros[1],"</td>";
echo "<td>",$iroiros[2],"</td>";
echo "<td>",$iroiros[3],"</td>";
echo "<td>";
print_r($iroiros[4]);
echo "</td>";
echo "</tr><tr>";
echo "<td>",gettype($iroiros[0]),"</td>";
echo "<td>",gettype($iroiros[1]),"</td>";
echo "<td>",gettype($iroiros[2]),"</td>";
echo "<td>",gettype($iroiros[3]),"</td>";
echo "</tr></table>";
mike3653.141Array ( [0] => mike [1] => tama [2] => kuro )
stringintegerdoublebooleanarray

"mike"は文字列、365は数値などですが、gettype() で確認することができます。

string, integer, double... などと返ります。floatにはなりません。

var_dump()で示される型の名前はstring, int, float,... などとなります。一貫性がありません。

array(5) {
  [0]=>
  string(4) "mike"
  [1]=>
  int(365)
  [2]=>
  float(3.14)
  [3]=>
  bool(true)
  [4]=>
  array(3) {
    [0]=>
    string(4) "mike"
    [1]=>
    string(4) "tama"
    [2]=>
    string(4) "kuro"
  }
}

valueの型に応じて処理をすることも可能です。

intなら+1したいというような時は

if (is_int($iroiros[1])) {
    $iroiros[1] += 1;
}

調べるときにはis_...を使います。こっちはdoubleでなくfloat、integerでなくintです。こちらはvar_dump()で示される型の名前に一致しています。

is_array() - 変数が配列かどうかを検査する
is_bool() - 変数が boolean であるかを調べる
is_callable() - 引数が、関数としてコール可能な構造であるかどうかを調べる
is_float() - 変数の型が float かどうか調べる
is_int() - 変数が整数型かどうかを検査する
is_null() - 変数が NULL かどうか調べる
is_numeric() - 変数が数字または数値形式の文字列であるかを調べる
is_object() - 変数がオブジェクトかどうかを検査する
is_resource() - 変数がリソースかどうかを調べる
is_scalar() - 変数がスカラかどうかを調べる
is_string() - 変数の型が文字列かどうかを調べる

空の配列をつくる

生成の時に要素が空であれば、要素数0の配列ができます。

$karas = array();
$karas = [];

要素数が0でも、番号を指定して代入すると要素数が勝手に増えてくれます。

番号が飛んでも許されますが、代入されないところには要素はできません。参照するとエラーになります。key=>value で番号もkeyとして扱いますから、「keyはあるけれども値がない」という状態はありえません。NULLという値をいれれば別ですが。

$karas = array();
$karas[0] = "000";
$karas[2] = "222";
$karas[3] = 333;
var_dump($karas);
// echo $karas[1]; //Notice:  Undefined offset:

実行結果はver_dump()の値です。

array(3) {
  [0]=>
  string(3) "000"
  [2]=>
  string(3) "222"
  [3]=>
  int(333)
}

要素数と初期値を指定する配列

PHPの配列はそもそもkey=>valueなので枠だけできることはありません。そこでfillを使います。

array array_fill ( int $start_index , int $num , mixed $value )
array array_fill_keys ( array $keys , mixed $value )

数値なら初期値は0にしたいということが多いですが、例なので、33としてみます。

$shiteiA = array_fill(0, 256, 33);
echo "$shiteiA[0] $shiteiA[1] ... $shiteiA[255]\n";
echo "要素数は",count($shiteiA);

実行結果、0から255までの256の要素がある配列ができます。

33 33 ... 33
要素数は256

開始番号の指定を0以外にすると

$shiteiB = array_fill(10, 3, 'spam');
print_r($shiteiB);

次のように開始番号までの番号はなくなります。

Array
(
    [10] => spam
    [11] => spam
    [12] => spam
)

keyが数値でない時はarray_fill_keys()を使います。

$names = array("mike","tama","kuro");
$shiteiC = array_fill_keys($names, 'name of cat');
print_r($shiteiC);
Array
(
    [mike] => name of cat
    [tama] => name of cat
    [kuro] => name of cat
)

繰り返し機構

for文は多くの言語で使える王道です。key=>valueが基本のPHPでは、次のforeachが合理的です。

for ($i = 0; 256>$i; $i++)
for ($i = 0; count($shiteiA)>$i; $i++)

keyが数値とわかっている必要があります。

$names = array("mike","tama","kuro");
for ($i = 0; count($names)>$i; $i++){
    echo $names[$i]," ";
}

実行結果です。

mike tama kuro 

次はforeachが欲しくなる場面です。

$shiteiB = array_fill(10, 3, 'spam');
for ($i = 10; 13>$i; $i++){
    echo $shiteiB[$i]," ";
}
spam spam spam 

forの詳細

マニュアルには次のようにあります。

1. 最初の式(式1)は、ループ開始時に無条件に 評価(実行)されます。
2. 各繰り返しの開始時に、式2が評価されます。 (値がTRUEが場合、ループ継続)
3. 各繰り返しの後、式3が評価(実行)されます。
4. 各式は空にすることもできますし、複数の式をカンマで区切って指定することもできます。
5. 式2 でカンマ区切りの式を使用すると、最後の式の結果でループ
6. 式2 を空にすると、無限実行ループになります

マニュアルの例には繰り返しのたびに要素数を求めるのが速度のネックになる場合に、次のようにする例がありました。外に書くよりも一体感はあります。

$names = array("mike","tama","kuro");
for ($i = 0,$length=count($names);$length>$i; $i++){
    echo $names[$i]," ";
}
mike tama kuro 

foreach

foreach (array_expression as $value)
foreach (array_expression as $key => $value)

基本的な使い方は、

$names = array("mike","tama","kuro");
foreach ($names as $name){
    echo $name," ";
}
mike tama kuro 

$nameには現在の要素の値が代入されています。そのため値を変更してももとの配列に影響しません。

$names = array("mike","tama","kuro");
foreach ($names as $name){
    echo $name," ";
    $name = "spam";
}
echo "\n";
foreach ($names as $name){
    echo $name," ";
}
mike tama kuro 
mike tama kuro 

値を変更したい場合は、&をつけます。

$names = array("mike","tama","kuro");
foreach ($names as &$name){
    echo $name," ";
    $name = "spam";
}
echo "\n";
foreach ($names as $name){
    echo $name," ";
}
unset($name);//最後の要素への参照を解除
mike tama kuro 
spam spam spam 

$keyも必要な時は、

$names = array("mike","tama","kuro");
foreach ($names as $key=>$name){
    echo $key,":",$name," ";
}
0:mike 1:tama 2:kuro 

リストとしての配列

Javascriptではリストと比較しましたが、PHPでも数値がkeyとなる配列では同様なことができます。

pushは配列の最後に要素を追加し、配列の長さを返します。

popは配列の最後の要素を削除し、削除した要素の値を返します。

saba,hokke,iwashi,shishamo をpushで配列に取り込んでpopで取り出して書き出します。

仕組みを理解するために、一回のpushごとに戻値と配列を書いていき、4つ読んだら一回のpopのごとに戻値と配列を書いています。

$fishes=['saba','hokke','iwashi','shishamo'];
$fishlist = [];
foreach($fishes as $fish){
    $ct = array_push($fishlist,$fish);
    echo $ct;
    foreach($fishlist as $key=>$val){
        echo ":[",$key,"=>",$val,"] ";
     }
    echo "\n";
}
while (count($fishlist)>0){
    $retv = array_pop($fishlist);
    echo $retv;
    foreach ($fishlist as $key=>$val){
        echo ":[",$key,"=>",$val,"] ";
     }
    echo "\n";
}
1:[0=>saba] 
2:[0=>saba] :[1=>hokke] 
3:[0=>saba] :[1=>hokke] :[2=>iwashi] 
4:[0=>saba] :[1=>hokke] :[2=>iwashi] :[3=>shishamo] 
shishamo:[0=>saba] :[1=>hokke] :[2=>iwashi] 
iwashi:[0=>saba] :[1=>hokke] 
hokke:[0=>saba] 
saba

複数の要素を追加できます。

$fishlist=['saba','hokke'];
$ct = array_push($fishlist,'iwashi','shishamo');
echo $ct,"\n";
print_r($fishlist);
4
Array
(
    [0] => saba
    [1] => hokke
    [2] => iwashi
    [3] => shishamo
)

配列の主要な関数

一つの関数で多様な使い方ができるようになっていること、そもそも数が多いことで把握しきれないと感じます。書いたのは一部です。

このまとめのスタートがjavascriptなので、数値のkeyでの使用法を基準に書き、key=>valueについては章を変えて書くつもりですが、PHPの配列の基本がそもそもkey=>valueなのでちょっとはみ出す部分があります。

また、sortについてはさらにあとで扱うつもりでいましたが、引っかかることがあって触れています。しかし、sort(),ksort(),usort()などが書かれていないなど不完全ではあります。

最後のarray[key]を除いて[,...]などは省略可能を表します。

戻値関数説明
intarray_push(array[,x[,x...]]) 末尾に要素を一つ以上追加
xarray_pop(array) 末尾の要素を削除して、その要素を返す。
intarray_unshift(array[,x[,x...]]) 一つ以上の要素を配列の最初に加える。数値のキーはゼロから振りなおされるが、リテラルのキーはそのまま。
xarray_shift(array) 配列の先頭から要素を一つ取り出す。 数値のキーはゼロから振りなおされるが、リテラルのキーはそのまま。
arrayarray_splice(array,offset,length,replaceelements) 配列 array から offset および length で指定された要素を削除し、配列 replacement でそれを置換
keyarray_search(x, array[, true]) x と等しい値を持つ最初の要素のkey(位置)を返す。該当する要素がなければ false を返す。
厳密な比較が必要なら第三引数として,trueを加える。
bool in_array(x, array [,true]) x と等しい値を持つ要素がある時にtrueを返す。なければfalseを返す。
厳密な比較が必要なら第三引数として,trueを加える。
intcount(array)
sizeof(array)
要素数を返す。
boolasort(array) キーと要素との関係を維持しつつ配列をソート インプレース (元のデータを演算結果で置換)
boolarsort(array) キーと要素との関係を維持しつつ配列を逆順でソート インプレース (元のデータを演算結果で置換)
arrayarray_reverse( array [,preserve_keys = FALSE] ) 新しい逆順の配列を返す。 preserve_keysが TRUE の場合は数値のキーを保持。 非数値のキーは、常に保持。
arrayarray_slice(array, offset[,length [,preserve_keys = FALSE]]) array の offset から length の長さの範囲の要素を返す。デフォルトで配列の数値キーを並べなおす。それを防ぎたいときにはTRUEと指定。
boolarray_multisort(array1,array2,array3) 複数の配列をソートする。左の関数の書き方は一例。array1,2にはsortのためのキーだけの配列(valueのみ使用)array1,2のsortに従いarray3も同様にsortされる。sort順やフラグはそれぞれオプションで指定できる。
boolcollator_sort(Collator,array [,flag])
collator_asort(Collator,array [,flag])
現在のロケールの規則にもとづいて配列をソートする。ロケールといっても、sort()にあるSORT_LOCALE_STRING フラグのものと異なり、日本語では辞書順をこなす。
unset(var [,var...])
unset(array[key])
変数操作関数。変数を破棄するものだが、配列の要素に使える。keyを指定して配列の要素を破棄するにはこれしかない。戻値はない。

pushu,pop,shift,unshiftの組み合わせ

unshiftは配列の最初に要素を追加し、配列の長さを返します。

shiftは配列の最初の要素を削除し、削除した要素の値を返します。

★値の参照(戻値はすべて x)
↑x [i]
x [0]← array →x [-1]
↑x [i] = x
★要素数の増減をともなう関数(戻値はnが要素数, xは値)
⇩n _splice(array,i,0,x)
n _unshift(array,x)⇨ array ⇦n _push(array,x)
x _shift(array)⇦ ⇨x _pop(array)
⇩x _splice(array,i,w)
⇩ unset(array[key])
$names = array("mike","tama","kuro");
unset($names[1]);
print_r($names);

数値keyではありがたさは出ないけど、実行結果は

Array ( [0] => mike [2] => kuro )

collator_asort()を含めた理由

日本語の辞書順(国語辞典の見出しの順番)に簡潔に書いておきます。

$jisho = array ( 0 => 'じしょ', 1 => 'じてん', 2 => 'してん', 3 => 'しんかい', 4 => 'しさん', 5 => 'じこく', );
echo "<p>元の配列</p><pre>";
print_r($jisho);
echo "</pre><p>sort後の配列(文字コード順)</p><pre>";
sort($jisho, $sort_flags =SORT_LOCALE_STRING );
print_r($jisho);
echo "</pre><p>collator_sort後の配列(辞書順)</p><pre>";
$coll = collator_create( 'ja_JP' );
collator_sort($coll,$jisho);
print_r($jisho);
echo "</pre>";

元の配列

Array
(
    [0] => じしょ
    [1] => じてん
    [2] => してん
    [3] => しんかい
    [4] => しさん
    [5] => じこく
)

sort後の配列(文字コード順)

Array
(
    [0] => しさん
    [1] => してん
    [2] => しんかい
    [3] => じこく
    [4] => じしょ
    [5] => じてん
)

collator_sort後の配列(辞書順)

Array
(
    [0] => じこく
    [1] => しさん
    [2] => じしょ
    [3] => してん
    [4] => じてん
    [5] => しんかい
)

collatorの実行のためには、PHPにintl拡張モジュールの追加が必要になるかもしれません。Internationalisation module(s) for PHP です。

Debian 9 (Strech)では、次のパッケージになっています。(php-intlを指定すると2つ入りますDebianの事情です)

php-intl (1:7.0+49)
php7.0-intl (7.0.30-0+deb9u1)

配列中の値を探して削除

array_search("検索値",配列)でkeyを得ることができます。配列中の要素(value)で"検索値"と一致するものを探して位置(key)を返します。見つからない時はFALSEが返ります。一つ見つかったらそれ以上は探しません。

array_splice(配列,開始位置,長さ,置換要素)は、開始位置から、長さだけの要素を削除して、そこに置換要素を挿入します。新しくできた配列を返します。長さを1にして置換要素を書かなければ、削除だけします。

$names = array("mike","tama","kuro","tora","tama",);
echo "start |";
foreach ($names as $key=>$name) echo $key,":",$name," ";
$todel = "tama";
$i = array_search($todel,$names);
array_splice($names,$i,1);
echo "\n",$todel," ",$i,"|";
foreach ($names as $key=>$name) echo $key,":",$name," ";
$todel = "tora";
$i = array_search($todel,$names);
array_splice($names,$i,1);
echo "\n",$todel," ",$i,"|";
foreach ($names as $key=>$name) echo $key,":",$name," ";
start |0:mike 1:tama 2:kuro 3:tora 4:tama 
tama 1|0:mike 1:kuro 2:tora 3:tama 
tora 2|0:mike 1:kuro 2:tama 

リストのリスト

PHPでの名前は配列で、しかもリストでもないのですが、番号をキーとして長さを変えられる配列をリストと呼ぶとという話です。

リストは関数を呼び出す時の引数や戻値にすると便利なことが多いですが、リストのリストを必要とする場面がありました。ウェブで使うPHPでは関数をつくったり戻値を考えたりすることは今まで余りありませんでしたが、Javascriptとの比較の意味でここでもやってみます。

必要があってというのはこんな場面です。3つのリストがあります。要素数が増減するかもしれないのでリストです。このリストが3つあって、これを別の関数に引数として渡します。現在はリスト3つですが将来的に増える可能性もあるので、こちらもリストにします。リストのリストです。

$basename = ["アイス","ヨーグルト"];
$sauce = ["ブルーベリー","黒蜜","練乳","チョコ"];
$topping = ["イチゴ","バナナ","小倉餡"];
$dessertmenu = [basename,sauce,topping];

basename,sauce,toppingという分類名は配列やリストでは失われて番号になりますが、実際に作成したのは数学関係のプログラムでしたので、0,1,2という連番で構いませんでした。必要なら文字列のkeyの配列を使います。

$example1 = $dessertmenu[2][0]+$dessertmenu[1][2]+$dessertmenu[0][0];
// イチゴ練乳アイス

basenameに"かき氷"を加えたければ、

array_push($dessertmenu[0],"かき氷");

drinkメニューを加えたければ、

array_push($dessertmenu,["コーヒー","紅茶","ソーダ"]);

で可能です。

$basename = ["アイス","ヨーグルト"];
$sauce = ["ブルーベリー","黒蜜","練乳","チョコ"];
$topping = ["イチゴ","バナナ","小倉餡"];
$dessertmenu = [$basename,$sauce,$topping];
$example1 = $dessertmenu[2][0].$dessertmenu[1][2].$dessertmenu[0][0];
echo "参照例 | ",$example1;

echo "\n追加前 | ";
foreach ($dessertmenu[0] as $key=>$name) echo $key,":",$name," ";
array_push($dessertmenu[0],"かき氷");
echo "\n追加後 | ";
foreach ($dessertmenu[0] as $key=>$name) echo $key,":",$name," ";
echo "\n飲物追加前 | ";
foreach ($dessertmenu as $submenu)
    foreach ($submenu as $key=>$name) echo $key,":",$name," ";
array_push($dessertmenu,["コーヒー","紅茶","ソーダ"]);
echo "\n飲物追加後 | ";
foreach ($dessertmenu as $submenu)
    foreach ($submenu as $key=>$name) echo $key,":",$name," ";
array_push($basename,"パスタ");
echo "\nパスタ追加 | ";
foreach ($dessertmenu as $submenu)
    foreach ($submenu as $key=>$name) echo $key,":",$name," ";

実行結果です。

参照例 | イチゴ練乳アイス
追加前 | 0:アイス 1:ヨーグルト 
追加後 | 0:アイス 1:ヨーグルト 2:かき氷 
飲物追加前 | 0:アイス 1:ヨーグルト 2:かき氷 0:ブルーベリー 1:黒蜜 2:練乳 3:チョコ 0:イチゴ 1:バナナ 2:小倉餡 
飲物追加後 | 0:アイス 1:ヨーグルト 2:かき氷 0:ブルーベリー 1:黒蜜 2:練乳 3:チョコ 0:イチゴ 1:バナナ 2:小倉餡 0:コーヒー 1:紅茶 2:ソーダ 
パスタ追加 | 0:アイス 1:ヨーグルト 2:かき氷 0:ブルーベリー 1:黒蜜 2:練乳 3:チョコ 0:イチゴ 1:バナナ 2:小倉餡 0:コーヒー 1:紅茶 2:ソーダ 

パスタ追加ではリストに入れたリストが、元のリストと同一でないことがわかります。

リストのマップ

「必要なら文字列のkeyの配列を使います」と言ってしまったので、マップ版です。

単に0,1,2という代わりに"base","sauce","topping"を使うだけです。

$basename = ["アイス","ヨーグルト"];
$sauce = ["ブルーベリー","黒蜜","練乳","チョコ"];
$topping = ["イチゴ","バナナ","小倉餡"];
$dessertmenu = ["base"=>$basename,"sauce"=>$sauce,"topping"=>$topping];
$example1 = $dessertmenu["topping"][0].$dessertmenu["sauce"][2].$dessertmenu["base"][0];
echo "参照例 | ",$example1;

echo "\n追加前 | ";
foreach ($dessertmenu["base"] as $key=>$name) echo $key,":",$name," ";
array_push($dessertmenu["base"],"かき氷");
echo "\n追加後 | ";
foreach ($dessertmenu["base"] as $key=>$name) echo $key,":",$name," ";
echo "\n飲物追加前 | ";
foreach ($dessertmenu as $submenu)
    foreach ($submenu as $key=>$name) echo $key,":",$name," ";
array_push($dessertmenu,["コーヒー","紅茶","ソーダ"]);
echo "\n飲物追加後 | ";
foreach ($dessertmenu as $submenu)
    foreach ($submenu as $key=>$name) echo $key,":",$name," ";
array_push($basename,"パスタ");
echo "\nパスタ追加 | ";
foreach ($dessertmenu as $submenu)
    foreach ($submenu as $key=>$name) echo $key,":",$name," ";

実行結果です。

参照例 | イチゴ練乳アイス
追加前 | 0:アイス 1:ヨーグルト 
追加後 | 0:アイス 1:ヨーグルト 2:かき氷 
飲物追加前 | 0:アイス 1:ヨーグルト 2:かき氷 0:ブルーベリー 1:黒蜜 2:練乳 3:チョコ 0:イチゴ 1:バナナ 2:小倉餡 
飲物追加後 | 0:アイス 1:ヨーグルト 2:かき氷 0:ブルーベリー 1:黒蜜 2:練乳 3:チョコ 0:イチゴ 1:バナナ 2:小倉餡 0:コーヒー 1:紅茶 2:ソーダ 
パスタ追加 | 0:アイス 1:ヨーグルト 2:かき氷 0:ブルーベリー 1:黒蜜 2:練乳 3:チョコ 0:イチゴ 1:バナナ 2:小倉餡 0:コーヒー 1:紅茶 2:ソーダ