はじめに
任意個の配列を受け取ってすべての組み合わせを計算する関数が必要だったので作りました。
参考記事とロジック
すべての組み合わせを計算するロジック自体は、既に様々な方が公開されています。
ここでは、配列同士の組み合わせを出力する(多重ループの展開)
を参考にさせていただきます。
function combine() {
$args = func_get_args();
$a = array_shift($args);
$b = array_shift($args);
$result = array();
foreach ($a as $val1) {
foreach ($b as $val2) {
$result[] = array_merge((array)$val1, (array)$val2);
}
}
if (count($args) > 0) {
foreach ($args as $arg) {
$result = combine($result, $arg);
}
}
return $result;
}
要件
任意個の配列を受け取って組み合わせを出すのは、すでにできています。
しかし、このままでは combine()
に最初に渡す配列は、固定で以下のように渡す必要があります。
combine([1, 2], [3, 4], [5, 6]);
関数を使う側が、渡す引数の数を意識せず、任意個の配列を渡せるようにできないでしょうか?
PHP5.6以降では
php5.6以降であれば、 ...
の表現を用いて、配列を展開して関数に渡すことができるので、
既存の関数は何もいじることなく、呼び出し時に配列を展開するだけでOKです。
combine(...[[1, 2], [3, 4], [5, 6]]);
#=> combine([1, 2], [3, 4], [5, 6]); と同様になる
展開機能を使わない場合
今回、業務で使用しているアプリケーションのPHPバージョンの事情で
この機能は使えなかったため、関数自体に手を加える必要がありました。
考え方としては、以下のような感じです。
- 固定長の引数を受け取る
- 引数は、結果の配列と、もとにする配列
コードは下記
function combine2($elements, $result=[]) {
if (empty($result)) {
$a = array_shift($elements);
$b = array_shift($elements);
} else {
$a = $result;
$result = [];
$b = array_shift($elements);
}
foreach ($a as $val1) {
foreach ($b as $val2) {
$result[] = array_merge((array)$val1, (array)$val2);
}
}
if (count($elements) > 0) {
$result = combine2($elements, $result);
}
return $result;
}
var_export(combine2([[1, 2], [3, 4], [5, 6]]));
/*
array (
0 =>
array (
0 => 1,
1 => 3,
2 => 5,
),
1 =>
array (
0 => 1,
1 => 3,
2 => 6,
),
2 =>
array (
0 => 1,
1 => 4,
2 => 5,
),
3 =>
array (
0 => 1,
1 => 4,
2 => 6,
),
4 =>
array (
0 => 2,
1 => 3,
2 => 5,
),
5 =>
array (
0 => 2,
1 => 3,
2 => 6,
),
6 =>
array (
0 => 2,
1 => 4,
2 => 5,
),
7 =>
array (
0 => 2,
1 => 4,
2 => 6,
),
)
*/
終わりに
以上です。やりたかったこと以上に、配列の展開機能の便利さをすごく感じました。