此包已被弃用且不再维护。作者建议使用 mpetrovich/dash 包。

PHP的函数式编程库。受Underscore、Lodash和Ramda的启发。

v4.0.1 2021-09-07 15:01 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.";

跳转到

操作

在此处查看操作的全列表

亮点

为什么使用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的一个bug,通常不能与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']

反馈

发现了bug或有建议?请创建一个新的GitHub问题。我们想要您的反馈!