talesoft/tale-iterator

一个基本的、PSR-7 兼容的流实用程序库

0.1.1 2018-08-24 12:42 UTC

This package is auto-updated.

Last update: 2024-09-10 08:04:48 UTC


README

Packagist License CI Coverage

Tale Iterator

什么是 Tale Iterator?

Tale Iterator 通过一些更实用的迭代器扩展了 SPL 迭代器,适用于常见用例。

安装

composer require talesoft/tale-iterator

使用

MapIterator

通过 map() 方法映射值。你通常想要扩展它并重写 map() 方法。

使用 FlipIterator 栈来映射键(见本 README 底部的 "如何做 XYZ")

use Tale\Iterator\MapIterator;

$values = new \ArrayIterator(range(0, 5));
$mapper = new class($values) extends MapIterator
{
    public function map()
    {
        return sprintf('Value %d', parent::map());
    }
};

var_dump(iterator_to_array($mapper));
/*
array(6) {
  [0] => string(7) "Value 0"
  [1] => string(7) "Value 1"
  [2] => string(7) "Value 2"
  [3] => string(7) "Value 3"
  [4] => string(7) "Value 4"
  [5] => string(7) "Value 5"
}
*/

CallbackMapIterator

通过指定一个简单的回调来映射值。

使用 FlipIterator 栈来映射键(见本 README 底部的 "如何做 XYZ")

use Tale\Iterator\CallbackMapIterator;

$values = new \ArrayIterator(range(0, 5));
$mapper = new CallbackMapIterator($values, function (int $number) {
    return sprintf('Value %d', $number);
});

var_dump(iterator_to_array($mapper));
/*
array(6) {
  [0] => string(7) "Value 0"
  [1] => string(7) "Value 1"
  [2] => string(7) "Value 2"
  [3] => string(7) "Value 3"
  [4] => string(7) "Value 4"
  [5] => string(7) "Value 5"
}
*/

FilterIterator

通过 accept() 方法过滤值。你通常想要扩展它并重写 accept() 方法。**它将保留键**!如果你想重置键,请按照以下示例链式一个 ValueIterator

使用 FlipIterator 栈来过滤键(见本 README 底部的 "如何做 XYZ")

use Tale\Iterator\FilterIterator;

$values = new \ArrayIterator(range(0, 5));
$filterer = new class($values) extends FilterIterator
{
    public function accept(): bool
    {
        return parent::current() !== 4;
    }
};

var_dump(iterator_to_array($filterer));
/*
array(5) {
  [0] => string(7) "Value 0"
  [1] => string(7) "Value 1"
  [2] => string(7) "Value 2"
  [3] => string(7) "Value 3"
  [5] => string(7) "Value 5"
}
*/

CallbackFilterIterator

通过指定一个简单的回调来过滤值。

使用 FlipIterator 栈来过滤键(见本 README 底部的 "如何做 XYZ")

use Tale\Iterator\CallbackFilterIterator;

$values = new \ArrayIterator(range(0, 5));
$filterer = new CallbackFilterIterator($values, function (int $number) {
    return $number !== 3;
});

var_dump(iterator_to_array($filterer));
/*
array(5) {
  [0] => string(7) "Value 0"
  [1] => string(7) "Value 1"
  [2] => string(7) "Value 2"
  [4] => string(7) "Value 4"
  [5] => string(7) "Value 5"
}
*/

注意

PHP 已经有了 FilterIterator 和 CallbackFilterIterator,但它只接受 \Iterator 的实例,不包括 \IteratorAggregate 实例。这个接受 \Traversable 的实例,它包括所有可迭代的对象(包括对象和原生数组,也在此范围内)。尽管如此,它仍然使用与 PHP 实现相同的 API!

IterableIterator

这是一个将任何 iterable 转换为有效迭代器的小型实用程序迭代器。它相当于一个 \IteratorIterator,它将传递的可迭代对象标准化为

$iterable instanceof \Traversable ? $iterable : new \ArrayIterator($iterable).

使用这个迭代器,你可以轻松地将任何类型的可迭代对象(如数组、对象、生成器等)传递给只接受 \Iterator 实例的迭代器。

这对于 PHP 的 SPL 迭代器或其他只利用 iterable\Traversable 并仅依赖于 \Iterator 的迭代器实现非常有用,或者出于非常好的原因这样做。

use Tale\Iterator\IterableIterator;

$values = new IterableIterator(['a', 'b', 'c', 'd', 'e']);
$filterer = new \RegexIterator($values, '/[a-c]/');

var_dump(iterator_to_array($filterer));
/*
array(3) {
  [0] => string(1) "a"
  [1] => string(1) "b"
  [2] => string(1) "c"
}
*/

ValueIterator

这是迭代器的 array_values()。这对于重置 FilterIterator 输出的键非常有用。

use Tale\Iterator\CallbackFilterIterator;
use Tale\Iterator\ValueIterator;

$values = new \ArrayIterator(range(0, 5));
$filterer = new CallbackFilterIterator($values, function (int $number) {
    return $number !== 3;
});
$resetter = new ValueIterator($filterer);

var_dump(iterator_to_array($resetter));
/*
array(5) {
  [0] => string(7) "Value 0"
  [1] => string(7) "Value 1"
  [2] => string(7) "Value 2"
  [3] => string(7) "Value 4"
  [4] => string(7) "Value 5"
}

Compare the output to the CallbackFilterIterator example above
and notice the keys!
*/

KeyIterator

这是迭代器的 array_keys()。如果你想得到内部迭代器键的干净列表,这非常有用。

use Tale\Iterator\KeyIterator;

$values = new \ArrayIterator(['a' => 1, 'b' => 2, 'c' => 3]);
$keys = new KeyIterator($values);

var_dump(iterator_to_array($keys));
/*
array(3) {
  [0] => string(1) "a"
  [1] => string(1) "b"
  [2] => string(1) "c"
}

Compare the output to the CallbackFilterIterator example above
and notice the keys!
*/

FlipIterator

这个迭代器将键和值翻转。如果你想让外部迭代器在键上而不是在值上操作,这通常很有用。

由于迭代器的工作方式,只要你没有将迭代器展开为数组,重复的值不会导致键丢失!注意第二个示例来理解我的意思。

use Tale\Iterator\FlipIterator;

$values = new \ArrayIterator(range('a', 'e'));
$flipper = new FlipIterator($values);

var_dump(iterator_to_array($flipper));
/*
array(5) {
  'a' => int(0)
  'b' => int(1)
  'c' => int(2)
  'd' => int(3)
  'e' => int(4)
}
*/

使用 array_flip,重复的值会导致键丢失,因为数组键必须是唯一的。对于迭代器来说,只要你不实际展开它,这种情况就不会发生!

use Tale\Iterator\FlipIterator;

$values = new \ArrayIterator(['a' => 1, 'b' => 2, 'c' => 2, 'd' => 2]);
$flipper = new FlipIterator($values);

//Do something with $flipper, like, iterator stuff

$reverseFlipper = new FlipIterator($flipper);

var_dump(iterator_to_array($reverseFlipper));
/*
array(4) {
  'a' => int(1)
  'b' => int(2)
  'c' => int(2)
  'd' => int(2)
}
*/

FormatIterator

这是迭代器中每个值的 sprintf($format, $current)

使用 FlipIterator 栈来格式化键(见本 README 底部的 "如何做 XYZ")

use Tale\Iterator\FormatIterator;

$values = new \ArrayIterator(range(0, 5));
$formatter = new FormatIterator($values, 'Value %d');

var_dump(iterator_to_array($formatter));
/*
array(6) {
  [0] => string(7) "Value 0"
  [1] => string(7) "Value 1"
  [2] => string(7) "Value 2"
  [3] => string(7) "Value 3"
  [4] => string(7) "Value 4"
  [5] => string(7) "Value 5"
}
*/

PrefixIterator

这是迭代器中每个值的 $prefix.$current

使用 FlipIterator 栈来前缀键(见本 README 底部的 "如何做 XYZ")

use Tale\Iterator\PrefixIterator;

$values = new \ArrayIterator(range(0, 5));
$prefixer = new PrefixIterator($values, 'Value ');

var_dump(iterator_to_array($prefixer));
/*
array(6) {
  [0] => string(7) "Value 0"
  [1] => string(7) "Value 1"
  [2] => string(7) "Value 2"
  [3] => string(7) "Value 3"
  [4] => string(7) "Value 4"
  [5] => string(7) "Value 5"
}
*/

SuffixIterator

这是迭代器中每个值的 $current.$suffix

使用FlipIterator将后缀键堆叠(参见本README底部“如何进行XYZ”)

use Tale\Iterator\SuffixIterator;

$values = new \ArrayIterator(range(0, 5));
$suffixer = new SuffixIterator($values, ' Value');

var_dump(iterator_to_array($suffixer));
/*
array(11) {
  [0] => string(7) "0 Value"
  [1] => string(7) "1 Value"
  [2] => string(7) "2 Value"
  [3] => string(7) "3 Value"
  [4] => string(7) "4 Value"
  [5] => string(7) "5 Value"
}
*/

IndexIterator

这个迭代器在迭代过程中计算一个独立的索引并将其提供出来。这通常用于计算迭代的次数。ValueIteratorKeyIterator使用这个来重置键。

use Tale\Iterator\IndexIterator;

$values = new \ArrayIterator(['a' => 'b', 'b' => 'c', 'c' => 'd']);
$indexer = new IndexIterator($values);

foreach ($indexer as $key => $value) {
    $i = $indexer->getIndex();
    echo "{$key} => {$value} - at index: {$i}\n";
}
/*
a => b - at index: 0
b => c - at index: 1
c => d - at index: 2
*/

如何进行XYZ?

如何映射键而不是值?

很简单,通过链式连接MapIterator和FlipIterators!注意这不会产生任何额外的开销,除了函数调用。内部数组根本不会被复制或修改。

use Tale\Iterator\FlipIterator;
use Tale\Iterator\CallbackMapIterator;

$values = new \ArrayIterator(['a' => 1, 'b' => 2, 'c' => 3, 'd' => 4]);
$mapper = new FlipIterator(
    new CallbackMapIterator(
        new FlipIterator($values),
        function (string $key) {
            return "Key {$key}";
        }
    )
);

var_dump(iterator_to_array($mapper));
/*
array(4) {
  'Key a' => int(1)
  'Key b' => int(2)
  'Key c' => int(3)
  'Key d' => int(4)
}
*/

如何过滤键而不是值?

在这里,FlipIterator同样可以做你需要的一切!

use Tale\Iterator\FlipIterator;
use Tale\Iterator\CallbackFilterIterator;

$values = new \ArrayIterator(['a' => 1, 'b' => 2, 'c' => 3, 'd' => 4]);
$mapper = new FlipIterator(
    new CallbackFilterIterator(
        new FlipIterator($values),
        function (string $key) {
            return $key !== 'b';
        }
    )
);

var_dump(iterator_to_array($mapper));
/*
array(4) {
  'a' => int(1)
  'c' => int(3)
  'd' => int(4)
}
*/