mpetrovich / dash
PHP的函数式编程库。灵感来自Underscore, Lodash, 和 Ramda。
Requires
- php: >=7.4
Requires (Dev)
- phpdocumentor/phpdocumentor: ^3.0
- phpunit/phpunit: ^9.5
- squizlabs/php_codesniffer: ^3.6
- dev-main
- v4.0.1
- v4.0.0
- v3.1.0
- v3.0.0
- v2.1.0
- v2.0.1
- v2.0.0
- v2.0.0-alpha
- v1.1.0
- v1.0.1
- v1.0.0
- v0.14.0
- v0.13.0
- v0.12.2
- v0.12.1
- v0.12.0
- v0.11.2
- v0.11.1
- v0.11.0
- v0.10.0
- v0.9.1
- v0.9.0
- v0.8.1
- v0.8.0
- v0.7.1
- v0.7.0
- v0.6.0
- v0.5.0
- v0.4.0
- v0.3.0
- v0.2.0
- v0.1.0
- dev-dependabot/composer/symfony/http-kernel-5.4.20
- dev-dependabot/composer/twig/twig-2.14.11
- dev-dependabot/composer/symfony/framework-bundle-5.4.4
- dev-generators
This package is auto-updated.
Last update: 2024-08-30 01:54:43 UTC
README
PHP的函数式编程库。灵感来自Underscore, Lodash, 和 Ramda。
$avgMaleAge = Dash\chain([ ['name' => 'John', 'age' => 12, 'gender' => 'male'], ['name' => 'Jane', 'age' => 34, 'gender' => 'female'], ['name' => 'Pete', 'age' => 23, 'gender' => 'male'], ['name' => 'Mark', 'age' => 11, 'gender' => 'male'], ['name' => 'Mary', 'age' => 42, 'gender' => 'female'], ]) ->filter(['gender', 'male']) ->map('age') ->average() ->value(); echo "Average male age is $avgMaleAge.";
跳转到
操作
亮点
- 支持多种数据类型:数组、对象、生成器(即将推出)、
Traversable
、DirectoryIterator
等 - 链式调用
- 柯里化
- 懒计算
- 自定义操作
- 经过良好测试:包含近3000个测试用例和100%代码覆盖率
为什么使用Dash?
PHP的内置array_*
函数有限制、难以组合、不一致,并且不适用于多种数据类型。
例如,假设我们要找出以下列表中男性的平均年龄
$people = [ ['name' => 'John', 'age' => 12, 'gender' => 'male'], ['name' => 'Jane', 'age' => 34, 'gender' => 'female'], ['name' => 'Pete', 'age' => 23, 'gender' => 'male'], ['name' => 'Mark', 'age' => 11, 'gender' => 'male'], ['name' => 'Mary', 'age' => 42, 'gender' => 'female'], ];
使用PHP的内置函数,我们可能会写出这样的代码
$males = array_filter($people, function ($person) { return $person['gender'] === 'male'; }); $avgMaleAge = array_sum(array_column($males, 'age')) / count($males);
Dash简化了常见的数据转换操作
$avgMaleAge = Dash\chain($people) ->filter(['gender', 'male']) ->map('age') ->average() ->value();
这仅仅是Dash能做的很小一部分。在此处查看操作的完整列表。
安装
需要PHP 7.4+
composer require mpetrovich/dash
使用方法
Dash操作是纯函数,可以单独使用或链式调用。
独立
操作可以像命名空间函数一样调用
Dash\map([1, 2, 3], function ($n) { return $n * 2; }); // === [2, 4, 6]
或作为静态方法
use Dash\Dash; Dash::map([1, 2, 3], function ($n) { return $n * 2; }); // === [2, 4, 6]
Dash\_
也可以用作Dash\Dash
的别名
use Dash\_; _::map([1, 2, 3], function ($n) { return $n * 2; }); // === [2, 4, 6]
链式调用
可以使用chain()
按顺序链式调用多个操作。调用value()
以返回最终值。
$result = Dash\chain([1, 2, 3, 4, 5]) ->filter('Dash\isOdd') ->map(function ($n) { return $n * 2; }) ->value(); // $result === [2, 6, 10]
要将值显式转换为数组或stdClass
,请分别使用arrayValue()
或objectValue()
$result = Dash\chain(['a' => 1, 'b' => 2, 'c' => 3]) ->filter('Dash\isOdd') ->mapValues(function ($n) { return $n * 2; }) ->objectValue(); // $result === (object) ['a' => 2, 'c' => 6]
为了方便,可以使用addGlobalAlias()
将Dash\chain()
别名为全局函数。它只需要在应用程序引导过程中调用一次
// In your application bootstrap: Dash::addGlobalAlias('__'); // Elsewhere: $result = __([1, 2, 3, 4, 5]) ->filter('Dash\isOdd') ->map(function ($n) { return $n * 2; }) ->value();
有时您不需要链式调用的返回值。但是,直到调用value()
之前,链式调用不会处理。为了语义上的便利,run()
也是value()
的别名
$chain = Dash\chain(range(1, 5)) ->reverse() ->each(function ($n) { echo "T-minus $n...\n"; sleep(1); }); // Nothing echoed yet $chain->value(); // or $chain->run(); // Echoes each of the following lines 1 second apart: // T-minus 5... // T-minus 4... // T-minus 3... // T-minus 2... // T-minus 1...
支持的数据类型
Dash可以与多种数据类型一起工作,包括
- 数组
- 对象(例如
stdClass
) - 生成器(仍在开发中)
- 实现
Traversable
接口的任何内容 DirectoryIterator
,它也是Traversable
,但由于PHP的一个错误,通常不能与iterator_to_array()
一起使用。Dash可以透明地解决这个问题。
示例
使用数组
Dash\chain([1, 2, 3, 4]) ->filter('Dash\isEven') ->map(function ($value) { return $value * 2; }) ->value(); // === [4, 8]
使用对象
Dash\chain((object) ['a' => 1, 'b' => 2, 'c' => 3, 'd' => 4]) ->filter('Dash\isOdd') ->keys() ->join(', ') ->value(); // === 'a, c'
使用Traversable
Dash\chain(new ArrayObject(['a' => 1, 'b' => 2, 'c' => 3, 'd' => 4])) ->pick(['b', 'c']) ->values() ->sum() ->value(); // === 5
使用DirectoryIterator
$iterator = new FilesystemIterator(__DIR__, FilesystemIterator::SKIP_DOTS); $filenames = Dash\chain($iterator) ->reject(function ($fileinfo) { return $fileinfo->isDir(); }) ->map(function ($fileinfo) { return pathinfo($fileinfo)['filename']; }) ->value();
柯里化
curry()
和相关操作可以用于从任何可调用函数创建柯里化函数
function listThree($a, $b, $c) { return "$a, $b, and $c"; } $listThree = Dash\curry('listThree'); $listTwo = $listThree('first'); $listTwo('second', 'third'); // === 'first, second, and third'
大多数 Dash 函数都有一个柯里化版本,它将输入数据作为最后一个参数接受,而不是作为第一个参数。柯里化版本位于 Dash\Curry
命名空间中
Dash\chain([ 'a' => 3, 'b' => '3', 'c' => 3, 'd' => 3.0 ]) ->filter(Dash\Curry\identical(3)) ->value(); // === ['a' => 3, 'c' => 3]
同样,partial()
和相关操作可以用于创建部分应用函数
$greet = function ($greeting, $name) { return "$greeting, $name!"; }; $sayHello = Dash\partial($greet, 'Hello'); $sayHowdy = Dash\partial($greet, 'Howdy'); $sayHello('Mark'); // === 'Hello, Mark!' $sayHowdy('Jane'); // === 'Howdy, Jane!'
懒计算
链式操作仅在调用 value()
或 run()
时才会评估。此外,可以使用 with()
修改并多次评估输入数据。这使得创建可重用的链式操作变得简单
$chain = Dash\chain() ->filter('Dash\isOdd') ->map(function ($n) { return $n * 2; }); $chain->with([1, 2, 3])->value(); // === [2, 6] $chain->with([4, 5, 6, 7])->value(); // === [10, 14]
链式操作也可以被克隆和扩展
// …continued from above $clone = clone $chain; $clone->map(function ($n) { $n + 1; }) $clone->value(); // === [11, 15] // The original chain is untouched $chain->value(); // === [10, 14]
当调用 value()
时,结果将被缓存,直到链式操作被修改或使用 with()
修改输入。
自定义操作
可以使用 setCustom()
、getCustom()
和 unsetCustom()
分别添加、检索和删除自定义操作。Dash\custom()
也是 Dash::getCustom()
的别名
Dash::setCustom('triple', function ($n) { return $n * 3; }); // Standalone Dash::triple(4); // === 12 // Chained Dash\chain([1, 2, 3]) ->sum() ->triple() ->value(); // === 18 // As an argument Dash\chain([1, 2, 3]) ->map('Dash\Dash::triple') ->value(); // === [3, 6, 9] // As an argument using the Dash::getCustom() method Dash\chain([1, 2, 3]) ->map(Dash::getCustom('triple')) ->value(); // === [3, 6, 9] // Using the Dash\custom() operation Dash\chain([1, 2, 3]) ->map(Dash\custom('triple')) ->value(); // === [3, 6, 9] Dash::unsetCustom('triple');
在链式操作中,当前输入被传递为自定义操作的第一个参数
Dash::setCustom('divide', function($numerator, $denominator) { return $numerator / $denominator; }); Dash\chain(6)->divide(2)->value(); // === 2
技巧
如果您发现 Dash 没有您需要的操作,不必担心。无需放弃 Dash 链式或其他功能,可以添加自定义逻辑。通过 Dash\thru()
操作整合缺失的操作是最简单的方式,它允许自定义逻辑修改结果并无缝传递到链式操作中的下一步。
例如,假设我们想使用 array_change_key_case()
并保持通常的 Dash 链式语义。使用 thru()
,这很简单
$result = Dash\chain(['one' => 1, 'two' => 2, 'three' => 3]) ->filter('Dash\isOdd') ->thru(function($input) { return array_change_key_case($input, CASE_UPPER); }) ->keys() ->value(); // $result === ['ONE', 'THREE']
或者,如果您经常需要使用 array_change_key_case()
,可能更好的做法是添加一个新的自定义操作
Dash::setCustom('keyCase', function ($input, $case) { return array_change_key_case($input, $case); });
然后您可以使用它像其他任何可链式 Dash 方法一样
$result = Dash\chain(['one' => 1, 'two' => 2, 'three' => 3]) ->filter('Dash\isOdd') ->keyCase(CASE_UPPER) ->keys() ->value(); // $result === ['ONE', 'THREE']
反馈
发现错误或有一些建议?请 创建一个新的 GitHub 问题。我们希望得到您的反馈!