PHP的函数式编程库。灵感来自Underscore, Lodash, 和 Ramda。


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.";

跳转到

操作

在此处查看操作的完整列表

亮点

为什么使用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可以与多种数据类型一起工作,包括

示例

使用数组

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 问题。我们希望得到您的反馈!