Kintarou'sBlog

プログラミング学習中。学習内容のアウトプットや読書で学んだことなど随時投稿!

【PHP】array_map, array_filter, 連想配列に関する関数

こんにちは😊Kintarouです。

現在エンジニア転職を目指してTECH CAMPにてプログラミング学習中です👨‍🎓
夢はフリーランスエンジニアになって働く人が働きやすいシステムを作ること!
と、愛する妻と海外移住すること🗽

プログラミングや読んでいる本のことなど、ブログに書いていきます!
twitter : https://twitter.com/ryosuke_angry


今回参考にさせて頂いたサイト様🙇‍♂️
dotinstall.com
qiita.com


配列の各要素に関数を適用させる

array_mapを使うことで配列の各要素に関数を適用できます。

<?php

$prices = [100, 200, 300];

#第1引数に関数、第2引数に配列を渡します。
$newPrices = array_map(
  function ($n) { return $n * 1.1; },
  $prices
);

print_r($newPrices);

#=>Array
#=>(
#=>    [0] => 110
#=>    [1] => 220
#=>    [2] => 330
#=>)

※無名関数の処理がreturnで値を返すだけの場合、アロー関数が使えます。
function($n){ return $n * 1.1; },

fn($n) => $n * 1.1,

配列の中で条件に合う要素を抽出する

array_filterを使うことで、配列中の要素に条件式を渡してtrueのもののみを抽出できます。

<?php

$numbers = range(1, 10);

#array_mapとは逆で第1引数に配列、第2引数に関数を渡します。

$evenNumbers = array_filter(
  $numbers,
  fn($n) => $n % 2 === 0
);
  
  
print_r($evenNumbers);

#=>Array
#=>(
#=>    [1] => 2
#=>    [3] => 4
#=>    [5] => 6
#=>    [7] => 8
#=>    [9] => 10
#=>)

※添字も抽出されている点にも注意です。

配列のキー、値を操作する

関数名 配列の処理
array_keys 配列のキーを抽出する
array_values 配列の値を抽出する
array_key_exists 指定したキーが配列にあるかを真偽値で返す
in_array 指定した値が配列にあるかを真偽値で返す
array_seach 指定した値が配列にある場合、対応するキーを返す
<?php

$scores = [
  'japanese' => 80,
  'english'  => 70,
  'math'     => 60,
];

#キーを抽出します。
print_r(array_keys($scores));

#=>Array
#=>(
#=>    [0] => japanese
#=>    [1] => english
#=>    [2] => math
#=>)

#値を抽出します。
print_r(array_values($scores));

#=>Array
#=>(
#=>    [0] => 80
#=>    [1] => 70
#=>    [2] => 60
#=>)

#配列の中にenglishというキーがあった場合trueを出力します。
if (array_key_exists('english', $scores) === true) {
  echo 'true' . PHP_EOL;
}

#=>true

#配列の中に80という値があった場合trueを出力します。
if (in_array(80, $scores) === true) {
  echo 'true' . PHP_EOL;
}

#=>true

#配列の中に60というキーがあった場合、対応するキーを出力します。
echo array_search(60, $scores) . PHP_EOL;

#=>math

キーを含んだ配列をソートする

配列の並び替えには前回記事のsortとrsortが使えますが、値の順番がソートされてキーは省略されてしまいます。
ryosuke-toyama.hatenablog.com

<?php

$scores = [
  'japanese' => 80,
  'english'  => 70,
  'math'     => 60,
];

sort($scores);
print_r($scores);

#=>Array
#=>(
#=>    [0] => 60
#=>    [1] => 70
#=>    [2] => 80
#=>)

rsort($scores);
print_r($scores);

#=>Array
#=>(
#=>    [0] => 80
#=>    [1] => 70
#=>    [2] => 60
#=>)

キーを含めたままソートをする場合、以下の関数を使います。

関数名 配列の処理
asort キーを保持したまま値を昇順で並び替える
arsort キーを保持したまま値を降順で並び替える
ksort キーを昇順で並び替える
krsort キーを降順で並び替える
<?php

$scores = [
  'japanese' => 80,
  'english'  => 70,
  'math'     => 60,
];

asort($scores);
print_r($scores);

#=>Array
#=>(
#=>    [math] => 60
#=>    [english] => 70
#=>    [japanese] => 80
#=>)

arsort($scores);
print_r($scores);

#=>Array
#=>(
#=>    [japanese] => 80
#=>    [english] => 70
#=>    [math] => 60
#=>)


ksort($scores);
print_r($scores);

#=>Array
#=>(
#=>    [english] => 70
#=>    [japanese] => 80
#=>    [math] => 60
#=>)
#キーの頭文字が昇順になっています。

krsort($scores);
print_r($scores);

#=>Array
#=>(
#=>    [math] => 60
#=>    [japanese] => 80
#=>    [english] => 70
#=>)
#キーの頭文字が降順になっています。

ソート方法を定義する

上記以外に自分でソート方法を定義したい場合にusortという関数が使えます。
今回は先の例で出た$scoresをキーの文字数順に並び替えてみます。

<?php

#字数が同じだった場合の挙動を確認するためscience(=english)を入れてみます。
#キーの文字数順になっている事がわかるようにするため点数を変えました。
$scores = [
  'japanese' => 60,
  'english'  => 80,
  'science' => 50,
  'math'     => 70,
];

#キーをソートするのでusortではなくuksortとします。
#第2引数に無名関数でソート条件を記述します。
#配列を$a、$bそれぞれに代入し、総当たりで比較していきます。
#もし差異がない場合は0を返し、並び替えを行いません。
#字数が大きい場合1を返す、小さい場合に-1を返す、これを総当たりで行い字数の小さい順で並び替えを行います。

uksort(
  $scores,
  function ($a, $b) {
    if (strlen($a) === strlen($b)){
      return 0;
    }
    return strlen($a) > strlen($b) ? 1 : -1;
  }
  );

print_r($scores);

#=>Array
#=>(
#=>    [math] => 70
#=>    [science] => 50
#=>    [english] => 80
#=>    [japanese] => 60
#=>)

同じ条件で並んだ要素を更にソートする

仮に配列の中にキーと値を含んだ以下のような配列があったとします。

<?php

$data = [
  ['subject' => 'japanese', 'score' => 60],
  ['subject' => 'english', 'score' => 80],
  ['subject' => 'science', 'score' => 50],
  ['subject' => 'math', 'score' => 70],
];

この配列に対してsubjectの長さで並び替えをしても、usortではenglishとscienceの並び替えを行えません。
そこで、array_multisortを使ってsubjectの字数で並び替えた後に同じ字数のものを点数順でソートします。
ただし、いきなりarray_multisortでsubjectの字数をソート出来ないので字数の要素を入れ込みます。

<?php

$data = [
  ['subject' => 'japanese', 'score' => 60],
  ['subject' => 'english', 'score' => 80],
  ['subject' => 'science', 'score' => 50],
  ['subject' => 'math', 'score' => 70],
];


#foreach文で各配列にsub_strlen => (subject文字数)の連想配列を追加します。 ※1
foreach ($data as &$a) {
  $a += array('sub_strlen' => strlen($a['subject']));
}
unset($a);

#一度正しく追加出来ているか確認します。
print_r($data);

#=>Array
#=>(
#=>    [0] => Array
#=>        (
#=>            [subject] => japanese
#=>            [score] => 60
#=>            [sub_strlen] => 8
#=>        )
#=>
#=>    [1] => Array
#=>        (
#=>            [subject] => english
#=>            [score] => 80
#=>            [sub_strlen] => 7
#=>        )
#=>
#=>    [2] => Array
#=>        (
#=>            [subject] => science
#=>            [score] => 50
#=>            [sub_strlen] => 7
#=>        )
#=>
#=>    [3] => Array
#=>        (
#=>            [subject] => math
#=>            [score] => 70
#=>            [sub_strlen] => 4
#=>        )
#=>
#=>)


#続いて、array_multisortで文字数ソート後に点数ソートを行うので、それぞれのキーを抽出した配列を作成します。※2

$sub_strlens = array_column($data, 'sub_strlen');
$scores = array_column($data, 'score');

#文字数ソート後、点数ソートを行い出力します。

array_multisort(
  $sub_strlens,
  $scores,
  $data
);

print_r($data);

#=>Array
#=>(
#=>    [0] => Array
#=>        (
#=>            [subject] => math
#=>            [score] => 70
#=>            [sub_strlen] => 4
#=>        )
#=>
#=>   [1] => Array
#=>        (
#=>            [subject] => science
#=>            [score] => 50
#=>            [sub_strlen] => 7
#=>        )
#=>
#=>    [2] => Array
#=>        (
#=>            [subject] => english
#=>            [score] => 80
#=>            [sub_strlen] => 7
#=>        )
#=>
#=>    [3] => Array
#=>        (
#=>            [subject] => japanese
#=>            [score] => 60
#=>            [sub_strlen] => 8
#=>        )
#=>
#=>)

※1・・・foreachは仕様として、各配列のコピーに対して操作しているのでそのまま出力しても元配列は更新されません。
配列要素を更新するために、以下2点を行う必要があります。
- 更新したいforeach変数の頭に&を付与 (参照渡し)
- 更新処理後にunset(変数名)を記述 (参照渡しの解除)

※2・・・データからキーの値を抜き取る場合にarray_columnという関数を使っています。

長尺になりましたがこの辺で! 以上、どなたかの参考になれば幸いです😊