18
15

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

複数の配列やイテレータを同時にforeachする

Last updated at Posted at 2018-06-24

従来だとeach()で配列ポインタをぶん回したり、array_map(null)を使ったりするのが一般的な方法だった。

要約

  • each()はPHP8の頃には削除されるよ
  • arrayがだめならArrayIteratorでいいじゃん

今回の提案

parallel()ってヘルパー関数を作ってやります。

/**
 * @param iterable[] $iters    イテレータ/配列の組
 * @param int      $key_index  foreachのkeyになるindex
 * @param int|null $stop_index イテレーションを停止する条件のindex (無指定は$key_index)
 */
function parallel(array $iters, $key_index = 0, $stop_index = null)
{
    foreach ($iters as $i => $iter) {
        if (is_array($iter)) {
            $iters[$i] = new ArrayIterator($iter);
        }
    }

    if (count($iters) == 0) {
        throw new BadFunctionCallException();
    }

    $stop_index = $stop_index ?? $key_index;

    if (!isset($iters[$key_index])) {
        throw new OutOfRangeException();
    }

    if (!isset($iters[$stop_index])) {
        throw new OutOfRangeException();
    }

    while ($iters[$stop_index]->valid()) {
        $key = $iters[$key_index]->key();
        $current = [];
        foreach ($iters as $iter) {
            if ($iter->valid()) {
                $current[] = $iter->current();
                $iter->next();
            } else {
                $current[] = null;
            }
        }

        yield $key => $current;
    }
}

こうやって動かす。

<?php

$fruits = ['りんご', 'ばなな', 'みかん'];
$prices = [100, 140, 80];

foreach (parallel([$fruits, $prices]) as $key => [$fruit, $price]) {
    var_dump([$key =>compact('fruit', 'price')]);
}
// array(1) {
//   [0]=>
//   array(2) {
//     ["fruit"]=>
//     string(9) "りんご"
//     ["price"]=>
//     int(100)
//   }
// }
// ...

as $key => [$fruit, $num] の部分を as $key => $row にすれば、実際は array_map(null) と同じようになる。つまりはzip関数と同じ動作だ。

大事なポイント

  • array_map()と違って「配列以外も同時にイテレーションできる
    • ジェネレータなども同時に指定できる ←超重要
  • array_map(null)と同じように、長さが足りない場合はnullで埋められる
  • foreachのキーになるイテレータを指定できる (無指定では0)
  • foreachを停止する条件のイテレータを指定できる (無指定ではキーと同じ)
    • 迂闊にも無限リストを停止条件にすると永遠に停止しなくなるお

ねむいので続きはそのうち書く。

18
15
3

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
18
15

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?