LoginSignup
5
2

More than 5 years have passed since last update.

PythonのzipをPHPで

Last updated at Posted at 2016-07-08

まとめ

解はいろいろありそうですが、

$a = [1, 2, 3];
$b = [4, 5, 6];
$z = array_map(NULL, $a, $b);
print_r($z);

で十分。

Pythonのzipとは

2. 組み込み関数 — Python 2.7.x ドキュメント #zip

>> x = [1, 2, 3]
>> y = [4, 5, 6]
>> zipped = zip(x, y)
>> zipped
[(1, 4), (2, 5), (3, 6)]

自分の言葉にするとなんとなくこんなの

array内の各arrayの各n番目の値をまとめてarrayにした新しいarrayを返す。

array_map(NULL, $a, $b)とは

PHP: array_map - Manual

array_map() は、array1 の各要素に callback 関数を適用した後、 その全ての要素を含む配列を返します。 callback 関数が受け付けるパラメータの数は、 array_map() に渡される配列の数に一致している必要があります。


この関数の面白い使用方法として、 配列の配列を構築するというものがあります。 これは、コールバック関数の名前として NULL を使用することにより、簡単に実行できるものです。

コールバックで各配列の値を受け取り処理した結果を返して新しい配列にす
るが、null受け取るだけで何も加工せず配列で返す。

おそらく、

function($a, $b){
    return array($a, $b);
}

と同義。(可変長引数対応版なので実際は...func_get_args)

array_map(NULL, $a, $b)に似ているとは

2. 組み込み関数 — Python 2.7.x ドキュメント #zip

引数が全て同じ長さの際には、zip() は初期値引数が None の map() と似ています。


返されるリストは引数のシーケンスのうち長さが最小のものの長さに切り詰められます。

PHP: array_map - Manual

通常、二つ以上の配列を使用する場合、 それらの長さは等しい必要があります。 これは、 コールバック関数が対応する要素に対して並行して適用されるためです。 配列の長さが等しくない場合、要素数の少ない配列は空の要素で拡張して、最も長い配列の要素数に合わせます。

長さが違う場合

Python

a = [1, 2, 3]
b = [4, 5]

print zip(a, b)

# [(1, 4), (2, 5)]
php
$a = [1, 2, 3];
$b = [4, 5];

var_dump(array_map(null, $a, $b));

/*
array(3) {
  [0]=>
  array(2) {
    [0]=>
    int(1)
    [1]=>
    int(4)
  }
  [1]=>
  array(2) {
    [0]=>
    int(2)
    [1]=>
    int(5)
  }
  [2]=>
  array(2) {
    [0]=>
    int(3)
    [1]=>
    NULL
  }
}
*/

zipは長さが足りなければ無視し、mapPHPなら足りない分をnullで補う。

よりzipに似せる

試案1

拡張されたnullがある配列は弾く

異なる長さに対応
$a = [1, 2, 3];
$b = [4, 5];


$map = array_map(null, $a, $b );

print_r((array_filter($map,function($array){
        return !in_array(null, $array);
})));

/*
Array
(
    [0] => Array
        (
            [0] => 1
            [1] => 4
        )

    [1] => Array
        (
            [0] => 2
            [1] => 5
        )

)
*/

ただし、PHPの配列はnullを入れることも可能なので、これでは拡張されたことを判定できない。

$a = [1, null, 3];
$b = [4, 5];


$map = array_map(null, $a, $b );

print_r((array_filter($map,function($array){
        return !in_array(null, $array);
})));

/*
Array
(
    [0] => Array
        (
            [0] => 1
            [1] => 4
        )
)
*/

array_mapでの拡張が判定できない。
そもそも拡張されるarray_map使わないほうがいいか

愚直に

nullを含めた最短長に合わせる
$a = [1, null, 3, 4];
$b = [5, 6,    7];

function zip(){

        $args = func_get_args();
        // 添字は捨てるので扱いやすように数値に変換
        array_walk($args, function(&$arg) {
                $arg = array_values($arg);
        });
        $r =[];
        do {
                $tmp = [];
                foreach($args as &$arg) {
                        if(!array_key_exists(0, $arg)) {
                                return $r;
                        }
                        $tmp[] = array_shift($arg);
                }
                $r[] = $tmp;
        } while(true);
}

print_r(zip($a, $b));

/*
Array
(
    [0] => Array
        (
            [0] => 1
            [1] => 5
        )

    [1] => Array
        (
            [0] =>
            [1] => 6
        )

    [2] => Array
        (
            [0] => 3
            [1] => 7
        )

)
*/

添字の振り直しは入れた順番なので、数値と文字列が混合していても順番に0から振られる。

$iを消せると思いarray_shift()にしたが、foreachが参照渡しになりarray_shift()は毎回添字の振り直しが発生する。
$iを使うならwhile(++$i)array_key_exists($i, $arg)$tmp[] = $arg[$i]
そもそも$iを使わないならdo whileでなくwhileでよい。

array_columnを使って

PHP5.5以上で。
今更ですが5.6以上なら可変長引数...が使えますね。

array_columnバージョン
$a = [1, null, 3, 4];
$b = [5, 6,    7];

function zip(){

    $args = func_get_args();

    array_walk($args, function($arg) {
        $arg = array_values($arg);
    });

    $min = array_reduce($args, function($min, $array){
        return min($min, count($array));
    }, PHP_INT_MAX);
    $r = [];
    $i = -1;
    while(++$i < $min) {
        $r[] = array_column($args, $i);
    }
    return $r;
}

print_r(zip($a, $b));

/*
Array
(
    [0] => Array
        (
            [0] => 1
            [1] => 5
        )

    [1] => Array
        (
            [0] =>
            [1] => 6
        )

    [2] => Array
        (
            [0] => 3
            [1] => 7
        )

)
*/

参考記事

python zip phpなどで検索

書いた後に見つけて

大体が同じ長さの配列でテストしているので、異なる長さの対応はそんなに考えなくていいのかもしれません。
要素が減るほうが使いにくいとも思いますし。

5
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
2