PHPerのための配列処理100本ノック

2021/03/05に公開2

はじめに

「配列」は、プログラミング初学者の最初の関門のひとつであり、
そして一生の付き合いでもあります。
本記事は、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

daichidaichi

とても良い勉強になりました!ありがとうございます!!!

qlql

自分の勘違いだったらすみません。
問題18の回答は、':id' => 99,となるべきところ、'::id' => 99,の出力になってしまう箇所があると思います。