PHPで配列をn等分する(そして余りをどうするか)
初めまして、 kagata です。『バシャログ。』に記事を書く人を募集していると聞きつけて(?)、はるばる京都からここ横浜まで引っ越してまいりました。以後よろしくお願いいたします。
さて、記念すべきデビュー戦となる今回は、PHPで配列をいくつかに分割するという操作をするにあたって考えたことをご紹介します。そう難しくない話なのですが、どうぞおつきあいください。
例題:10個の要素を持つ配列を4個の部分配列に分割せよ
はい。お察しのとおり、タイトルで「等分する」とうたっておきながら等分できっこない例題を立てました。余りが出る場合どうするかをあわせて検討しようということです。では始めます。
方法1. array_chunk() を使う
PHPには配列を分割するビルトイン関数として、 array_chunk() が用意されています。
配列を、要素数が size の配列に分割します。 最後の部分の要素数は size より小さくなることもあります。
ここで注意しないといけないのは、引数として与えるのが「分割する部分配列の個数」ではなく「部分配列が持つ要素数」であるということです。そのため、分割数から部分配列の要素数を計算する必要があります。
ということで、次のような関数を書いてみました。
function array_divide($arr, $division) {
$count = ceil(count($arr) / $division); // 部分配列1個あたりの要素数
$ret = array_chunk($arr, $count);
return $ret;
}
この関数で要素数10の配列を4分割してみました。すると、以下のような配列が得られます。
array(4) {
[0]=>
array(3) {
[0]=>
int(1)
[1]=>
int(2)
[2]=>
int(3)
}
[1]=>
array(3) {
[0]=>
int(4)
[1]=>
int(5)
[2]=>
int(6)
}
[2]=>
array(3) {
[0]=>
int(7)
[1]=>
int(8)
[2]=>
int(9)
}
[3]=>
array(1) {
[0]=>
int(10)
}
}
このとおり、4分割できました。
しかし…最後の部分配列の要素数だけが1個と、ちょっと中途半端です。3-3-3-1ではなく、3-3-2-2のようにならして分割できないでしょうか。ということで、さらに考えました。
方法2. array_slice() を使う
array_slice() は、配列の一部を切り出してくるビルトイン関数です。
array_slice()は、array から引数 offset および length で指定された連続する要素を返します。
array_chunk() による方法よりも多少手数はかかりますが、これを使って部分配列を1つずつ地道に切り出してみることにします。
function array_divide($array, $division) {
$base_count = floor(count($array) / $division); // 部分配列1個あたりの要素数
$remainder = count($array) % $division; // 余りになる要素数
$ret = array();
$offset = 0;
for($i = 0; $i < $division; $i++) {
/*
* 余りの要素がある場合は、
* 先頭の部分配列に1個ずつまぶしていく
*/
if (empty($remainder)) {
$length = $base_count;
} else {
$length = $base_count + 1;
$remainder--;
}
$ret[] = array_slice($array, $offset, $length);
$offset += $length;
}
return $ret;
}
この関数で、先ほどと同じく要素数10の配列を4分割してみました。結果は次のとおり。
array(4) {
[0]=>
array(3) {
[0]=>
int(1)
[1]=>
int(2)
[2]=>
int(3)
}
[1]=>
array(3) {
[0]=>
int(4)
[1]=>
int(5)
[2]=>
int(6)
}
[2]=>
array(2) {
[0]=>
int(7)
[1]=>
int(8)
}
[3]=>
array(2) {
[0]=>
int(9)
[1]=>
int(10)
}
}
はい、バランスよく3-3-2-2に分割できました。
まとめ
- PHPで配列を分割するなら array_chunk() が便利
- でも余りをならして分割したいときは、 array_slice() で地道に切り出そう
- 引数には分割数ではなく分割した配列の要素数を指定しよう
- 配列をn等分するような関数はビルトインされてないのかしら。あんまり需要ない?