PythonのzipをPHPで

# <i class="fa fa-graduation-cap" aria-hidden="true"></i> まとめ

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

* [pythonのzip - Qiita]

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

で十分。


# <i class="fa fa-cube" aria-hidden="true"></i> Pythonのzipとは

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

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


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

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

# <i class="fa fa-cube" aria-hidden="true"></i> `array_map(NULL, $a, $b)`とは

[PHP: array_map - Manual]

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

***

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

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

おそらく、

```php
function($a, $b){
    return array($a, $b);
}
```
と同義。(可変長引数対応版なので実際は`...`や`func_get_args`)

# <i class="fa fa-cube" aria-hidden="true"></i> `array_map(NULL, $a, $b)`に似ているとは

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

>**引数が全て同じ長さの際には、**zip() は初期値引数が None の map() と似ています。
***
>返されるリストは引数のシーケンスのうち**長さが最小のものの長さに切り詰められます。**

[PHP: array_map - Manual]

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

## 長さが違う場合

```py:Python

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

print zip(a, b)

# [(1, 4), (2, 5)]
```

```php: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`は長さが足りなければ無視し、`map`は`PHP`なら足りない分を`null`で補う。

# <i class="fa fa-code" aria-hidden="true"></i> より`zip`に似せる

## 試案1

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

```php:異なる長さに対応
$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を入れることも可能なので、これでは拡張されたことを判定できない。

```php
$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`使わないほうがいいか


## 愚直に

```php: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以上なら可変長引数`...`が使えますね。

```php: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
        )

)
*/

```

# <i class="fa fa-book" aria-hidden="true"></i> 参考記事

*python zip php*などで検索 

* [pythonのzip - Qiita]
* [php 配列の縦横変換 - Qiita]
* [array_map(null, ...) で配列を結合 - Qiita]


書いた後に見つけて

* [Is there a php function like python's zip? - Stack Overflow]  
Rubyのzipは拡張する？というか拡張かつ切り捨てのような動作。  
[zip (Array) - Rubyリファレンス]  
こちらもチャンレンジしてみたい。  
よりpythonに合わせる方法もいろいろと。  
minの取り方がよりスマートなのがありますね。  
`array_slice`がNice。


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

[pythonのzip - Qiita]:http://qiita.com/ukisoft/items/bfeb439be673e2df328c
[php 配列の縦横変換 - Qiita]:http://qiita.com/cb400sp2/items/4b4715fcf91878dd8f51
[array_map(null, ...) で配列を結合 - Qiita]:http://qiita.com/fivestar/items/cca52a62515edcf72e3d
[zip (Array) - Rubyリファレンス]:http://ref.xaio.jp/ruby/classes/array/zip


[Is there a php function like python's zip? - Stack Overflow]:http://stackoverflow.com/questions/2815162/is-there-a-php-function-like-pythons-zip

[2. 組み込み関数 — Python 2.7.x ドキュメント #zip]:http://docs.python.jp/2/library/functions.html#zip
[PHP: array_map - Manual]:http://php.net/manual/ja/function.array-map.php