talesoft/tale-collection

dev-master 2018-09-10 19:06 UTC

This package is auto-updated.

Last update: 2024-09-11 15:33:50 UTC


README

Packagist License CI Coverage

Tale Collection

什么是Tale Collection?

Tale Collection 是一个针对PHP的基于迭代器的Collection实现。

它包含一个通用的Collection类,该类可以很好地处理链式迭代,并提供了Map和Set实现,API稳定。

安装

composer require talesoft/tale-collection

用法

Collection

签名

Tale\collection(iterable $iterable): Tale\CollectionInterface
//or
new Tale\Collection(iterable $iterable)

示例

use function Tale\collection;

$collection = collection(['a', 'b', 'c', 'd']))
    ->filter(function (string $char) {
        return $char !== 'b';
    })
    ->map(function (string $char) {
        return strtoupper($char)
    })
    ->flip();
    
var_dump($collection['c']); //int(2)
var_dump($collection->toArray());
/*
array(3) {
  'a' => int(0)
  'c' => int(2)
  'd' => int(3)
}
*/

如上所述,它高度基于迭代器,因此除非实际访问数据,否则许多堆叠操作永远不会将任何内容转换为实际的数组。

让我们演示一下!

use function Tale\collection;

funuction generateAtoZ()
{
    //Imagine this is some hard work
    yield from range('a', 'z');
}

$collection = collection(generateAtoZ())
    //We filter all a-s
    ->filter(function (string $char) {
        return $char !== 'a';
    })
    //We also chain a normal SPL RegexIterator and drop all i to z-s
    ->chain(\RegexIterator::class, '/[^i-z]/')
    //Uppercase all characters
    ->map(function (string $char) {
        return strtoupper($char);
    })
    //Flip the keys and values
    ->flip()
    //Drop characters with the index 3 (this will be 'C')
    ->filter(function (int $index) {
        return $index !== 2;
    })
    //Flip again
    ->flip()
    //Only yield the values (array_values, basically)
    ->getValues();
    
//This right below is the _first_ part where generateAtoZ will
//actually be triggered at all! Until then we basically did nothing
//except for chaining some iterators

var_dump($collection[2]); //string(1) "E"

//Notice how it's E, because we dropped A and C

var_dump($collection->toArray());
/*
array(6) {
  [0] => string(1) "B"
  [1] => string(1) "D"
  [2] => string(1) "E"
  [3] => string(1) "F"
  [4] => string(1) "G"
  [5] => string(1) "H"
}
*/

大多数 Tale Collection的方法会返回另一个Collection。Collection不仅包含数组,还可以包含一个可迭代对象。可迭代对象会通过您使用的几乎所有方法传递,只有在您访问键或将其转换为数组时才会懒加载。

这为深度修改可迭代值提供了可能,而无需在所有地方创建数组副本。

实际上,上述示例中除了toAray()之外,没有调用任何方法触发了generateAtoZ()生成器,甚至包括getValues()flip()。它们都返回包含嵌套特定迭代器的集合。

这还导致了一些很好的副作用,比如flip()不会覆盖值。

想象一下,我们想在翻转数组时处理键,而不会覆盖其值(非常节省内存)

use function Tale\collection;

$values = collection(['a' => 2, 'b' => 2, 'c' => 3])
    ->flip()
    ->map(function (string $key) {
        return strtoupper($key);
    })
    ->flip();
    
var_dump($values->toArray());
/*
array(3) {
  'A' => int(2)
  'B' => int(2)
  'C' => int(3)
}
*/

要了解发生了什么,让我们看看原生的PHP等效代码

use function Tale\collection;

$values = array_flip(['a' => 2, 'b' => 2, 'c' => 3]);
//Here our values are lost already, it will result in
// [2 => 'b', 3 => 'c'], as keys are unique

$values = array_map(function (string $key) {
    return strtoupper($key);
}, $values);

$values = array_flip(['a' => 2, 'b' => 2, 'c' => 3]);

var_dump($values);
/*
array(2) {
  ["B"]=> int(2)
  ["C"]=> int(3)
}
*/

由于迭代器的工作方式,值在检索最终结果之前永远不会被简化为实际的数组,重复键在迭代过程中不会有问题,并且始终可用,直到可迭代对象在内部转换为数组。

特殊化的集合

以下所有集合都使用与上述讨论的集合类相同的迭代器机制。实际上,它们都实现了Tale\CollectionInterface,并使用一个公共基类Tale\AbstractCollection来定义它们的大部分API。

Set

Set类是一组值的抽象,其中键不重要。Set的键将得到管理,并且始终保持顺序。

值将是唯一的。如果值已存在于Set中,则在使用add时不会再次添加。

内容通过以下API管理

Set->has($item)

检查集合是否包含特定项

Set->add($item)

向集合添加新项

Set->remove($item)

从集合中删除项

use function Tale\set;

$existingObject = new SomeClass();

$set = set();
$set->add(new SomeOtherClass());

var_dump($set->has($existingObject)); //bool(true)

var_dump($set->toArray());
/*
array(3) {
  0 => object(SomeOtherClass)#1 (0) {}
  1 => object(SomeClass)#2 (0) {}
}
*/

常见用例

元素上的CSS类集合

use function Tale\set;

$classList = set(['btn']);

if ($primary) {
    $classList->add('btn-primary');
}

if ($block) {
    $classList->add('btn-block');
}

if ($classList->has('primary') {
    echo "This is a primary button!\n";
}

echo "<button class=\"{$classList->join(' ')}\">My button!</button>";

Map

Map是一种数据结构,允许使用除了字符串和整数以外的键。由于PHP引擎的限制,您不能通过数组访问访问复杂键([$obj])。

以下API管理Map

Map->get($key)

检索特定键的值

Map->set($key, $value)

为特定键设置值

Map->has($key)

检查特定键是否存在

Map->remove($key)

从Map中删除具有特定键的值

use function Tale\map;

$key1 = new Key1();
$key2 = new Key2();

$map = map();
$map->set($key1, 'value 1');
$map->set($key2, 'value 2');
    
var_dump($map->toArray());
/*
array(3) {
  0 => array(2) {
    0 => object(SomeOtherClass)#1 (0) {}
    1 => string(7) "value 1"
  }
  1 => array(2) {
    0 => object(SomeClass)#2 (0) {}
    1 => string(7) "value 2"
  }
}
*/

常见用例

对象的元数据存储

use function Tale\map;

$metadata = map();

$b = new B();

$objects = [
    new A(),
    $b,
    new C(),
    new D()
];

foreach ($objects as $obj) {
    $metadata->set($obj, getMetadataForObject($obj));
}

//[...]

if ($metadata->has($b)) {
    $bMetadata = $metadata->get($b);
    
    //$bMetadata is now the metadata for the $b instance
}

待办事项:更多文档。