PHPerのための配列処理100本ノック
はじめに
「配列」は、プログラミング初学者の最初の関門のひとつであり、
そして一生の付き合いでもあります。
本記事は、PHPで配列を操作する演習問題集です。
ここに掲載している問題は全て for / foreach を使えば解けます。
初学者の方は、すぐにスマートな方法が思いつかなければ、
まずは for / foreach を使って解決してみてください。
それだけでも、十分配列の理解は深まります。
ただし、ここに掲載している問題は全て for / foreach を使わずに解けます。
ステップアップを目指す方は、ループ処理を使わない方法も考えてみてください。
使用方法
各問題のソースコードを手元のエディタに全文コピペします。
末尾の var_dump() の出力が、コメントの通りになるように、
// SOME CODE HERE // の箇所に、
処理を記述してください。
難易度:低
問題1
ユーザマスタから取得した2次元配列 $in から、
名前だけを取り出した配列を作ってください。
$in = [
[
'id' => 1,
'code' => 'S1001',
'name' => '山田',
],
[
'id' => 2,
'code' => 'S1003',
'name' => '鈴木',
],
[
'id' => 3,
'code' => 'S1002',
'name' => '佐藤',
],
];
//////////////////////////////////
// SOME CODE HERE //
//////////////////////////////////
var_dump($out);
/*
array (size=3)
0 => string '山田' (length=6)
1 => string '鈴木' (length=6)
2 => string '佐藤' (length=6)
*/
回答例
$out = array_column($in, 'name');
問題2
ユーザマスタの1行を取得した連想配列 $in から、
連想配列のキーのみを抽出した配列を作成してください。
$in = [
'id' => 1,
'code' => 'S1001',
'name' => '山田',
'pref' => 27,
];
//////////////////////////////////
// SOME CODE HERE //
//////////////////////////////////
var_dump($out);
/*
array (size=4)
0 => string 'id' (length=2)
1 => string 'code' (length=4)
2 => string 'name' (length=4)
3 => string 'pref' (length=4)
*/
回答例
$out = array_keys($in);
問題3
$in は、ユーザマスタから取得した2次元配列です。
ユーザコードをキー、名前を値とする連想配列に加工してください。
$in = [
[
'id' => 1,
'code' => 'S1001',
'name' => '山田',
],
[
'id' => 2,
'code' => 'S1003',
'name' => '鈴木',
],
[
'id' => 3,
'code' => 'S1002',
'name' => '佐藤',
],
];
//////////////////////////////////
// SOME CODE HERE //
//////////////////////////////////
var_dump($out);
/*
array (size=3)
'S1001' => string '山田' (length=6)
'S1002' => string '鈴木' (length=6)
'S1003' => string '佐藤' (length=6)
*/
回答例
$out = array_column($in, 'name', 'code');
問題4
$in1 は ユーザコード => 名前 の連想配列です。
名前 $in2 をもとに該当するユーザコードを取得してください。
$in1 = [
'S1001' => '山田',
'S1002' => '鈴木',
'S1003' => '佐藤',
];
$in2 = '鈴木';
//////////////////////////////////
// SOME CODE HERE //
//////////////////////////////////
var_dump($out);
/*
string 'S1002' (length=5)
*/
回答例
$out = array_search($in2, $in1);
問題5
$in1 はユーザコードの配列、
$in2 は名前の配列です。
ユーザコードをキー、名前を値とした連想配列を作成してください。
$in1 = ['S1001', 'S1002', 'S1003'];
$in2 = ['山田', '鈴木', '佐藤'];
//////////////////////////////////
// SOME CODE HERE //
//////////////////////////////////
var_dump($out);
/*
array (size=3)
'S1001' => string '山田' (length=6)
'S1002' => string '鈴木' (length=6)
'S1003' => string '佐藤' (length=6)
*/
回答例
$out = array_combine($in1, $in2);
問題6
中身が全て $in1 で、要素数が $in2 個の配列を作ってください。
$in1 = 'x';
$in2 = 5;
//////////////////////////////////
// SOME CODE HERE //
//////////////////////////////////
var_dump($out);
/*
array (size=5)
0 => string 'x' (length=1)
1 => string 'x' (length=1)
2 => string 'x' (length=1)
3 => string 'x' (length=1)
4 => string 'x' (length=1)
*/
回答例
$out = array_fill(0, $in2, $in1);
難易度:中
問題7
配列 $in は自然数または null を含む配列です。
この配列から null を取り除いてください。
加工後の配列は、連番の添字配列となるようにしてください。
$in = [1, 2, null, 4, null, 6, 7];
//////////////////////////////////
// SOME CODE HERE //
//////////////////////////////////
var_dump($out);
/*
array (size=5)
0 => int 1
1 => int 2
2 => int 4
3 => int 6
4 => int 7
*/
回答例
$out = array_values(array_filter($in));
※このコードの場合、空文字や 0 や false も取り除かれます。
問題8
$in は文字列型の数字が入った配列です。
中身を全て整数型にしてください。
$in = ['1', '2', '3', '4', '5'];
//////////////////////////////////
// SOME CODE HERE //
//////////////////////////////////
var_dump($out);
/*
array (size=5)
0 => int 1
1 => int 2
2 => int 3
3 => int 4
4 => int 5
*/
回答例
$out = array_map('intval', $in);
問題9
$in はユーザマスタから取得した2次元配列です。
sex = 2 のレコードだけに絞ってください。
$in = [
[
'id' => 1,
'name' => '山田',
'sex' => 2,
],
[
'id' => 2,
'name' => '鈴木',
'sex' => 2,
],
[
'id' => 3,
'name' => '佐藤',
'sex' => 1,
],
[
'id' => 4,
'name' => '小林',
'sex' => 2,
],
[
'id' => 5,
'name' => '伊藤',
'sex' => 1,
],
];
//////////////////////////////////
// SOME CODE HERE //
//////////////////////////////////
var_dump($out);
/*
array (size=3)
0 =>
array (size=3)
'id' => int 1
'name' => string '山田' (length=6)
'sex' => int 2
1 =>
array (size=3)
'id' => int 2
'name' => string '鈴木' (length=6)
'sex' => int 2
3 =>
array (size=3)
'id' => int 4
'name' => string '小林' (length=6)
'sex' => int 2
*/
回答例
$out = array_filter($in, function ($row) {
return $row['sex'] === 2;
});
問題10
$in はユーザマスタから取得した2次元配列です。
誕生日を YYYY年M月D日 表記に変更してください。
$in = [
[
'id' => 1,
'name' => '山田',
'birthday' => '1980-01-01',
],
[
'id' => 2,
'name' => '鈴木',
'birthday' => '1985-02-14',
],
[
'id' => 3,
'name' => '佐藤',
'birthday' => '1990-12-31',
],
];
//////////////////////////////////
// SOME CODE HERE //
//////////////////////////////////
var_dump($out);
/*
array (size=3)
0 =>
array (size=3)
'id' => int 1
'name' => string '山田' (length=6)
'birthday' => string '1980年1月1日' (length=15)
1 =>
array (size=3)
'id' => int 2
'name' => string '鈴木' (length=6)
'birthday' => string '1985年2月14日' (length=16)
2 =>
array (size=3)
'id' => int 3
'name' => string '佐藤' (length=6)
'birthday' => string '1990年12月31日' (length=17)
*/
回答例
$out = array_map(function ($row) {
$row['birthday'] = date('Y年n月j日', strtotime($row['birthday']));
return $row;
}, $in);
問題11
$in はテーブル tbl に追加したいデータであり、
カラム名 => 値 の連想配列となっています。
この配列からINSERT文を生成してください。
$in = [
'a' => 1,
'b' => 2,
'c' => 3,
'd' => 4,
];
//////////////////////////////////
// SOME CODE HERE //
//////////////////////////////////
var_dump($out);
/*
string 'INSERT INTO tbl(a, b, c, d) VALUES(1, 2, 3, 4)' (length=46)
*/
回答例
$into = join(', ', array_keys($in));
$values = join(', ', array_values($in));
$out = "INSERT INTO tbl({$into}) VALUES({$values})";
※もし $in がユーザ入力値 ( $_POST など ) であれば、この実装は不十分です。
キーの検証と値のエスケープをしなければ、SQLインジェクション等の攻撃を喰らいます。
難易度:高
問題12
$in1 はユーザマスタから取得した2次元配列です。
名前が $in2 であるレコードの、ユーザコードを取得してください。
$in1 = [
[
'id' => 1,
'code' => 'S1001',
'name' => '山田',
],
[
'id' => 2,
'code' => 'S1003',
'name' => '鈴木',
],
[
'id' => 3,
'code' => 'S1002',
'name' => '佐藤',
],
];
$in2 = '鈴木';
//////////////////////////////////
// SOME CODE HERE //
//////////////////////////////////
var_dump($out);
/*
string 'S1003' (length=5)
*/
回答例
$index = array_search($in2, array_column($in1, 'name'));
$out = $in1[$index]['code'] ?? null;
問題13
スネークケースをパスカルケースに変換してください。
$in = 'this_is_an_apple';
//////////////////////////////////
// SOME CODE HERE //
//////////////////////////////////
var_dump($out);
/*
string 'ThisIsAnApple' (length=13)
*/
回答例
$out = join('', array_map('ucfirst', explode('_', $in)));
問題14
$in はユーザマスタから取得した2次元配列です。
レコードに含まれる pref の値を全て重複なく抽出して、配列にしてください。
また、作成した配列は、添字配列(連番キー)となるようにしてください。
$in = [
[
'id' => 1,
'name' => '山田',
'pref' => 27,
],
[
'id' => 2,
'name' => '鈴木',
'pref' => 26,
],
[
'id' => 3,
'name' => '佐藤',
'pref' => 13,
],
[
'id' => 4,
'name' => '小林',
'pref' => 27,
],
[
'id' => 5,
'name' => '伊藤',
'pref' => 13,
],
];
//////////////////////////////////
// SOME CODE HERE //
//////////////////////////////////
var_dump($out);
/*
array (size=3)
0 => int 27
1 => int 26
2 => int 13
*/
回答例
$out = array_values(array_unique(array_column($in, 'pref')));
問題15
$in1 は tbl テーブルの更新内容、
$in2 は更新対象のIDです。
これらを元に UPDATE 文を作成してください。
$in1 = [
'a' => 1,
'b' => 2,
'c' => 3,
'd' => 4,
];
$in2 = 99;
//////////////////////////////////
// SOME CODE HERE //
//////////////////////////////////
var_dump($out);
/*
string 'UPDATE tbl SET a = 1, b = 2, c = 3, d = 4 WHERE id = 99' (length=55)
*/
回答例
$set = join(', ', array_map(function ($key, $value) {
return "{$key} = {$value}";
}, array_keys($in1), array_values($in1)));
$out = "UPDATE tbl SET {$set} WHERE id = {$in2}";
※もし $in がユーザ入力値 ( $_POST など ) であれば、この実装は不十分です。
値のエスケープをしなければ、SQLインジェクションを喰らいます。
キーの検証をしなければ、意図しない値が更新される脆弱性を生みます。
問題16
$in1 はユーザマスタから取得した2次元配列、
$in2 は都道府県マスタから取得した2次元配列です。
$in1 の各要素に都道府県名 pref_name を追加してください。
$in1 = [
[
'id' => 1,
'name' => '山田',
'pref' => 27,
],
[
'id' => 2,
'name' => '鈴木',
'pref' => 26,
],
[
'id' => 3,
'name' => '佐藤',
'pref' => 13,
],
[
'id' => 4,
'name' => '小林',
'pref' => null,
],
];
$in2 = [
[
'pref_id' => 13,
'pref_name' => '東京',
],
[
'pref_id' => 26,
'pref_name' => '京都',
],
[
'pref_id' => 27,
'pref_name' => '大阪',
],
[
'pref_id' => 28,
'pref_name' => '神戸',
],
];
//////////////////////////////////
// SOME CODE HERE //
//////////////////////////////////
var_dump($out);
/*
array (size=4)
0 =>
array (size=4)
'id' => int 1
'name' => string '山田' (length=6)
'pref' => int 27
'pref_name' => string '大阪' (length=6)
1 =>
array (size=4)
'id' => int 2
'name' => string '鈴木' (length=6)
'pref' => int 26
'pref_name' => string '京都' (length=6)
2 =>
array (size=4)
'id' => int 3
'name' => string '佐藤' (length=6)
'pref' => int 13
'pref_name' => string '東京' (length=6)
3 =>
array (size=4)
'id' => int 4
'name' => string '小林' (length=6)
'pref' => null
'pref_name' => null
*/
回答例
$prefs_assoc = array_column($in2, 'pref_name', 'pref_id');
$out = array_map(function ($row) use ($prefs_assoc) {
$row['pref_name'] = $prefs_assoc[$row['pref']] ?? null;
return $row;
}, $in1);
問題17
$in1 はユーザから POST されたデータの格納された連想配列です。
この連想配列から、ホワイトリスト $in2 に含まれないキーを全て削除してください。
$in1 = [
'id' => 2,
'xxx' => 'yyy',
'code' => 'S1003',
'name' => '鈴木',
'zzz' => 'www',
];
$in2 = ['id', 'code', 'name'];
//////////////////////////////////
// SOME CODE HERE //
//////////////////////////////////
var_dump($out);
/*
array (size=3)
'id' => int 2
'code' => string 'S1003' (length=5)
'name' => string '鈴木' (length=6)
*/
回答例
$out = array_intersect_key($in1, array_flip($in2));
問題18
$in1 は tbl テーブルの更新内容、
$in2 は更新対象のIDです。
これらを元にプレースホルダ付の UPDATE 文と、実行用パラメータを作成してください。
$in1 = [
'a' => 1,
'b' => 2,
'c' => 3,
'd' => 4,
];
$in2 = 99;
//////////////////////////////////
// SOME CODE HERE //
//////////////////////////////////
var_dump($out1, $out2);
/*
string 'UPDATE tbl SET a = :a, b = :b, c = :c, d = :d WHERE id = :id' (length=60)
array (size=5)
':a' => int 1
':b' => int 2
':c' => int 3
':d' => int 4
':id' => int 99
*/
回答例
$set = join(', ', array_map(function ($key) {
return "{$key} = :{$key}";
}, array_keys($in1)));
$out1 = "UPDATE tbl SET {$set} WHERE id = :id";
$in1[':id'] = $in2;
$out2 = array_combine(
array_map(function ($key) {
return ':' . $key;
}, array_keys($in1)),
array_values($in1)
);
※もし $in が、例えば $_POST そのものである場合、この実装でも不十分です。
意図しないキーを送信され、意図しない値が更新される可能性があります。
前問のようなホワイトリスト処理を挟めば、実用に耐えるコードになります。
ここからが本題
ここに挙げた問題は、私が普段、
できれば foreach を使ってほしくない と感じるパターン集です。
コードを読んでいて foreach が現れた時、
そこから読み取れるのは、「配列がループ処理される」という情報だけです。
一方、array_filter() が現れれば、
「配列の各要素は変更せずに、一部の要素だけに絞り込んでいる」
array_map() が出てくれば、
「配列のキーと要素数は変更せずに、中身を加工しようとしている」
array_search() があれば、
「配列から1要素のみを抽出している」
というように、処理内容を端的に読み取ることができます。
これは、コードレビューをしたり、他人の書いた初見のコードに手を入れる際には、非常に大きな差になります。
Discussion
とても良い勉強になりました!ありがとうございます!!!
自分の勘違いだったらすみません。
問題18の回答は、
':id' => 99,
となるべきところ、'::id' => 99,
の出力になってしまう箇所があると思います。