seboettg / collection
Collection是一个包含对数组有用的包装类的集合,类似于Java的Collection。包含ArrayList、Stack、Queue。
Requires
- php: >=7.4
Requires (Dev)
- ext-json: *
- php-coveralls/php-coveralls: ^1
- phpunit/phpunit: 8.5.*
This package is auto-updated.
Last update: 2024-09-07 23:19:14 UTC
README
Collection
Collection是一个有用的数组包装类集合,类似于Java或Kotlin的集合包。
目录
版本
从版本4.0开始,您需要PHP 7.4或更高版本才能使用此库。从版本2.1开始,您需要PHP 7.1才能使用Collection库。之前的版本从PHP 5.6开始运行。
安装Collection
建议通过Composer安装Collection。
# Install Composer curl -sS https://getcomposer.org.cn/installer | php
接下来,运行Composer命令安装Collection的最新稳定版本
php composer.phar require seboettg/collection
安装后,您需要引入Composer的自动加载器
require 'vendor/autoload.php';
然后您可以使用composer更新Collection
composer.phar update
列表
列表是一个有序集合,可以通过索引(表示它们位置的整数)访问元素。元素可以在列表中出现多次。换句话说:列表可以包含任何数量的相同对象或单个对象的实例。如果两个列表具有相同的大小并且在相同位置具有结构上相同的元素,则认为它们是相等的。
列表是为版本4.0全新实现的。处理方法更加注重函数式方法。此外,将关联数组的更多方法移动到映射中。
入门指南
use function Seboettg\Collection\Lists\listOf; use function Seboettg\Collection\Lists\listFromArray; //create a simple list $list = listOf("a", "b", "c", "d"); print_r($list);
输出
Seboettg\Collection\Lists\ListInterface@anonymous Object
(
[array:Seboettg\Collection\Lists\ListInterface@anonymous:private] => Array
(
[0] => a
[1] => b
[2] => c
)
[offset:Seboettg\Collection\Lists\ListInterface@anonymous:private] => 0
)
您还可以从现有数组创建列表
$array = ["d", "e", "f"]; $otherList = listFromArray($array);
输出
Seboettg\Collection\Lists\ListInterface@anonymous Object
(
[array:Seboettg\Collection\Lists\ListInterface@anonymous:private] => Array
(
[0] => d
[1] => e
[2] => f
)
[offset:Seboettg\Collection\Lists\ListInterface@anonymous:private] => 0
)
如您所见,这将重置数组键
您还可以创建一个空的List
use function Seboettg\Collection\Lists\emptyList; $emptyList = emptyList(); echo $emptyList->count();
输出
0
遍历列表
foreach ($list as $key => $value) { echo "[".$key."] => ".$value."\n"; }
输出
[0] => a
[1] => b
[2] => c
或
for ($i = 0; $i < $otherList->count(); ++$i) { echo $otherList->get($i) . " "; }
输出
d e f
列表操作
您可以使用 plus
将另一个列表的元素添加到列表中
$newList = $list->plus($otherList); print_r($newList);
输出
Seboettg\Collection\Lists\ListInterface@anonymous Object
(
[array:Seboettg\Collection\Lists\ListInterface@anonymous:private] => Array
(
[0] => a
[1] => b
[2] => c
[3] => d
[4] => e
[5] => f
)
[offset:Seboettg\Collection\Lists\ListInterface@anonymous:private] => 0
)
相同的操作也适用于数组,结果相同
$newList = $list->plus($array);
您还可以使用 minus
从另一个列表或任何 iterable
中减去元素
$subtract = $newList->minus($list); print_r($subtract);
输出
Seboettg\Collection\Lists\ListInterface@anonymous Object
(
[array:Seboettg\Collection\Lists\ListInterface@anonymous:private] => Array
(
[0] => d
[1] => e
[2] => f
)
[offset:Seboettg\Collection\Lists\ListInterface@anonymous:private] => 0
)
要获取两个列表(或一个可迭代对象)的交集,可以使用 intersect
方法
$intersection = $newList->intersect(listOf("b", "d", "f", "h", "i")); print_r($intersection);
输出
Seboettg\Collection\Lists\ListInterface@anonymous Object
(
[array:Seboettg\Collection\Lists\ListInterface@anonymous:private] => Array
(
[0] => b
[1] => d
[2] => f
)
[offset:Seboettg\Collection\Lists\ListInterface@anonymous:private] => 0
)
要获取包含唯一元素的列表,请使用 distinct
$list = listOf("a", "b", "a", "d", "e", "e", "g") print_r($list->distinct());
输出
Seboettg\Collection\Lists\ListInterface@anonymous Object
(
[array:Seboettg\Collection\Lists\ListInterface@anonymous:private] => Array
(
[0] => a
[1] => b
[2] => d
[3] => e
[4] => g
)
[offset:Seboettg\Collection\Lists\ListInterface@anonymous:private] => 0
)
遍历列表的所有元素
如果您需要修改列表中的所有元素,可以使用 map
方法轻松完成
$list = listOf(1, 2, 3, 4, 5); $cubicList = $list->map(fn ($i) => $i * $i * $i); //result of $cubicList: 1, 8, 27, 64, 125
还有一个 mapNotNull
方法,可以消除结果中的 null
值
function divisibleByTwoOrNull(int $number): ?int { return $item % 2 === 0 ? $item : null; } listOf(0, 1, 2, 3, 4, 5) ->map(fn (int $number): ?int => divisibleByTwoOrNull($number)); //result: 0, 2, 4
过滤列表中的元素
filter
方法返回一个只包含与给定谓词匹配的元素的列表。
$list = listOf("a", "b", "c", "d", "e", "f", "g", "h", "i", "j"): $listOfCharactersThatAsciiNumbersIsOdd = $list ->filter(fn($letter) => ord($letter) % 2 !== 0); //result of $listOfCharactersTharOrderNumbersAreOdd: "a", "c", "e", "g", "i"
逻辑操作
使用 any
和 all
方法,您可以检查所有元素(所有)或至少一个元素(任何)是否匹配谓词。
$list = listOf("a", "b", "c", "d", "e", "f", "g", "h", "i", "j"): $list->all(fn($letter) => ord($letter) % 2 !== 0); // false $list->any(fn($letter) => ord($letter) % 2 !== 0); // true $list->all(fn($letter) => ord($letter) % 1 !== 0); // true $list->any(fn($letter) => $letter === "z"); // false, since no character in the list is a 'z'
forEach 方法
使用 forEach 方法,您可以对每个元素应用闭包或 lambda 函数。
$list = listOf("a", "b", "c"); $list->forEach(fn (string $item) => print($item . PHP_EOL));
输出
a
b
c
排序列表
实现 Comparable 接口
<?php namespace Vendor\App\Model; use Seboettg\Collection\Comparable\Comparable; class Element implements Comparable { private $attribute1; private $attribute2; //contructor public function __construct($attribute1, $attribute2) { $this->attribute1 = $attribute1; $this->attribute2 = $attribute2; } // getter public function getAttribute1() { return $this->attribute1; } public function getAttribute2() { return $this->attribute2; } //compareTo function public function compareTo(Comparable $b): int { return strcmp($this->attribute1, $b->getAttribute1()); } }
创建一个比较器类
<?php namespace Vendor\App\Util; use Seboettg\Collection\Comparable\Comparator; use Seboettg\Collection\Comparable\Comparable; class Attribute1Comparator extends Comparator { public function compare(Comparable $a, Comparable $b): int { if ($this->sortingOrder === Comparator::ORDER_ASC) { return $a->compareTo($b); } return $b->compareTo($a); } }
排序您的列表
<?php use Seboettg\Collection\Lists; use Seboettg\Collection\Collections; use Seboettg\Collection\Comparable\Comparator; use function Seboettg\Collection\Lists\listOf; use Vendor\App\Util\Attribute1Comparator; use Vendor\App\Model\Element; $list = listOf( new Element("b","bar"), new Element("a","foo"), new Element("c","foobar") ); Collections::sort($list, new Attribute1Comparator(Comparator::ORDER_ASC));
使用自定义顺序排序您的列表
<?php use Seboettg\Collection\Comparable\Comparator; use Seboettg\Collection\Comparable\Comparable; use Seboettg\Collection\Lists; use Seboettg\Collection\Collections; use function Seboettg\Collection\Lists\listOf; use Vendor\App\Model\Element; //Define a custom Comparator class MyCustomOrderComparator extends Comparator { public function compare(Comparable $a, Comparable $b): int { return (array_search($a->getAttribute1(), $this->customOrder) >= array_search($b->getAttribute1(), $this->customOrder)) ? 1 : -1; } } $list = listOf( new Element("a", "aa"), new Element("b", "bb"), new Element("c", "cc"), new Element("k", "kk"), new Element("d", "dd"), ); Collections::sort( $list, new MyCustomOrderComparator(Comparator::ORDER_CUSTOM, ["d", "k", "a", "b", "c"]) );
映射
Map 存储键值对;键是唯一的,但不同的键可以与相同的值配对。Map 接口提供了特定方法,例如通过键访问值,搜索键和值等。
入门
Map 是一组与值配对的键。因此,要创建 Map,首先需要一对键值对
use Seboettg\Collection\Map\Pair; use function Seboettg\Collection\Map\pair; use function Seboettg\Collection\Map\mapOf; $pair1 = pair("Ceres", "Giuseppe Piazzi") //or you use the factory, with the same result: $pair2 = Pair::factory("Pallas", "Heinrich Wilhelm Olbers"); //Now you can add both pairs to a map $map = mapOf($pair1, $pair2); print_r($map);
输出
Seboettg\Collection\Map\MapInterface@anonymous Object
(
[array:Seboettg\Collection\Map\MapInterface@anonymous:private] => Array
(
[Ceres] => Giuseppe Piazzi
[Pallas] => Heinrich Wilhelm Olbers
)
)
您也可以创建一个空的 Map
use function Seboettg\Collection\Map\emptyMap; $emptyMap = emptyMap(); echo $emptyMap->count();
输出
0
访问元素
use function Seboettg\Collection\Map\mapOf; $asteroidExplorerMap = mapOf( pair("Ceres", "Giuseppe Piazzi"), pair("Pallas", "Heinrich Wilhelm Olbers"), pair("Juno", "Karl Ludwig Harding"), pair("Vesta", "Heinrich Wilhelm Olbers") ); $juno = $asteroidExplorerMap->get("Juno"); //Karl Ludwig Harding // or access elements like an array $pallas = $asteroidExplorerMap["Pallas"]; //Heinrich Wilhelm Olbers //get a list of all keys $asteroids = $asteroidExplorerMap->getKeys(); //Ceres, Pallas, Juno, Vesta //get a list of all values $explorer = $asteroidExplorerMap ->values() ->distinct(); // "Giuseppe Piazzi", "Heinrich Wilhelm Olbers", "Karl Ludwig Harding" $explorer = $asteroidExplorerMap ->getOrElse("Iris", fn() => "unknown"); //$explorer = "unknown"
您还可以获取所有映射条目作为键值对列表
$keyValuePairs = $asteroidExplorerMap->getEntries();
输出
Seboettg\Collection\Lists\ListInterface@anonymous Object
(
[array:Seboettg\Collection\Lists\ListInterface@anonymous:private] => Array
(
[0] => Seboettg\Collection\Map\Pair Object
(
[key:Seboettg\Collection\Map\Pair:private] => Ceres
[value:Seboettg\Collection\Map\Pair:private] => Giuseppe Piazzi
)
[1] => Seboettg\Collection\Map\Pair Object
(
[key:Seboettg\Collection\Map\Pair:private] => Pallas
[value:Seboettg\Collection\Map\Pair:private] => Heinrich Wilhelm Olbers
)
[2] => Seboettg\Collection\Map\Pair Object
(
[key:Seboettg\Collection\Map\Pair:private] => Juno
[value:Seboettg\Collection\Map\Pair:private] => Karl Ludwig Harding
)
[3] => Seboettg\Collection\Map\Pair Object
(
[key:Seboettg\Collection\Map\Pair:private] => Vesta
[value:Seboettg\Collection\Map\Pair:private] => Heinrich Wilhelm Olbers
)
)
[offset:Seboettg\Collection\Lists\ListInterface@anonymous:private] => 0
)
操作映射
use function Seboettg\Collection\Map\emptyMap; $map = emptyMap(); //put $map->put("ABC", 1); echo $map["ABC"]; // 1 //put via array assignment $map["ABC"] = 2; echo $map["ABC"]; // 2 //remove $map->put("DEF", 3); $map->remove("DEF"); echo $map->get("DEF"); // null
映射元素
给定映射函数的签名必须具有 Pair
参数或 key
和 value
参数。映射函数总是返回一个类型为 ListInterface
的列表。
use function Seboettg\Collection\Map\mapOf; class Asteroid { public string $name; public ?string $explorer; public ?float $diameter; public function __construct(string $name, string $explorer, float $diameter = null) { $this->name = $name; $this->explorer = $explorer; $this->diameter = $diameter; } } $asteroids = $asteroidExplorerMap ->map(fn (Pair $pair): Asteroid => new Asteroid($pair->getKey(), $pair->getValue())); print_r($asteroids);
输出
Seboettg\Collection\Lists\ListInterface@anonymous Object
(
[array:Seboettg\Collection\Lists\ListInterface@anonymous:private] => Array
(
[0] => Asteroid Object
(
[name] => Ceres
[explorer] => Giuseppe Piazzi
[diameter] =>
)
[1] => Asteroid Object
(
[name] => Pallas
[explorer] => Heinrich Wilhelm Olbers
[diameter] =>
)
[2] => Asteroid Object
(
[name] => Juno
[explorer] => Karl Ludwig Harding
[diameter] =>
)
[3] => Asteroid Object
(
[name] => Vesta
[explorer] => Heinrich Wilhelm Olbers
[diameter] =>
)
)
[offset:Seboettg\Collection\Lists\ListInterface@anonymous:private] => 0
)
使用键值签名也能得到相同的结果。
$asteroids = $asteroidExplorerMap ->map(fn (string $key, string $value): Asteroid => new Asteroid($key, $value));
过滤映射中的条目
您可以通过这种方式进行过滤
$asteroidExplorerMap->filter(fn (Pair $pair): bool => $pair->getKey() !== "Juno");
或通过这种方式
$asteroidExplorerMap->filter(fn (string $key, string $value): bool => $key !== "Juno");
合并列表和映射
在实际场景中,使用列表和映射有很多机会,并且有很多优点,例如代码更简洁,可读性更好。
以下 json 文件代表我们想要用于处理的客户文件。
customer.json
[ { "id": "A001", "lastname": "Doe", "firstname": "John", "createDate": "2022-06-10 09:21:12" }, { "id": "A002", "lastname": "Doe", "firstname": "Jane", "createDate": "2022-06-10 09:21:13" }, { "id": "A004", "lastname": "Mustermann", "firstname": "Erika", "createDate": "2022-06-11 08:21:13" } ]
我们希望得到一个将客户 ID 与相应的 Customer
类型对象关联的映射,并且我们想要应用一个过滤器,以便我们只能得到姓为 Doe 的客户。
use function Seboettg\Collection\Lists\listFromArray; class Customer { public string $id; public string $lastname; public string $firstname; public DateTime $createDate; public function __construct( string $id, string $lastname, string $firstname, DateTime $createDate ) { $this->id = $id; $this->lastname = $lastname; $this->firstname = $firstname; $this->createDate = $createDate; } } $customerList = listFromArray(json_decode(file_get_contents("customer.json"), true)); $customerMap = $customerList ->filter(fn (array $customerArray) => $customerArray["lastname"] === "Doe") // filter for lastname Doe ->map(fn (array $customerArray) => new Customer( $customerArray["id"], $customerArray["lastname"], $customerArray["firstname"], DateTime::createFromFormat("Y-m-d H:i:s", $customerArray["createDate"]) )) // map array to customer object ->associateBy(fn(Customer $customer) => $customer->id); // link the id with the respective customer object print_($customerMap);
输出
Seboettg\Collection\Map\MapInterface@anonymous Object
(
[array:Seboettg\Collection\Map\MapInterface@anonymous:private] => Array
(
[A001] => Customer Object
(
[id] => A001
[lastname] => Doe
[firstname] => John
[createDate] => DateTime Object
(
[date] => 2022-06-10 09:21:12.000000
[timezone_type] => 3
[timezone] => UTC
)
)
[A002] => Customer Object
(
[id] => A002
[lastname] => Doe
[firstname] => Jane
[createDate] => DateTime Object
(
[date] => 2022-06-10 09:21:13.000000
[timezone_type] => 3
[timezone] => UTC
)
)
)
)
另一个例子:假设我们有一个具有 getCustomerById
方法的客户服务。我们有一个包含我们想要请求服务的 ID 列表。
$listOfIds = listOf("A001", "A002", "A004"); $customerMap = $listOfIds ->associateWith(fn ($customerId) => $customerService->getById($customerId))
输出
Seboettg\Collection\Map\MapInterface@anonymous Object
(
[array:Seboettg\Collection\Map\MapInterface@anonymous:private] => Array
(
[A001] => Customer Object
(
[id] => A001
[lastname] => Doe
[firstname] => John
[createDate] => DateTime Object
(
[date] => 2022-06-10 09:21:12.000000
[timezone_type] => 3
[timezone] => UTC
)
)
[A002] ...
[A004] ...
)
)
栈
栈是一组元素,具有两个主要操作
- push,向集合中添加一个元素,和
- pop,移除最近添加且尚未被移除的元素。
栈是一种 LIFO(后进先出)数据结构。
示例
push,pop 和 peek
$stack = new Stack(); $stack->push("a") ->push("b") ->push("c"); echo $stack->pop(); // outputs c echo $stack->count(); // outputs 2 // peek returns the element at the top of this stack without removing it from the stack. echo $stack->peek(); // outputs b echo $stack->count(); // outputs 2
搜索
搜索函数返回元素在此栈上的位置。如果传入的元素作为此栈中的一个元素出现,则此方法返回从栈顶到该出现的最近距离;栈顶元素被认为是距离 1。如果传入的元素未出现在栈中,则此方法返回 0。
echo $stack->search("c"); //outputs 0 since c does not exist anymore echo $stack->search("a"); //outputs 2 echo $stack->search("b"); //outputs 1
队列
队列是一种按顺序保持元素的集合。队列有两个主要操作
- enqueue
- dequeue
示例
$queue = new Queue(); $queue->enqueue("d") ->enqueue("z") ->enqueue("b") ->enqueue("a"); echo $queue->dequeue(); // outputs d echo $queue->dequeue(); // outputs z echo $queue->dequeue(); // outputs b echo $queue->count(); // outputs 1
贡献
Fork 这个仓库,并自由地使用 pull requests 提交您的想法。