techworker/php-fn-sortby

一个小型函数,可用于对任何形状的数组进行排序。仅支持PHP7。

dev-master 2016-07-06 22:19 UTC

This package is auto-updated.

Last update: 2024-08-25 10:17:54 UTC


README

准备一个排序函数,可用于与 usort 结合使用,以函数式方式对任何形状的数组进行排序。

sortBy(..)->thenBy(..)->thenBy(.., \SORT_DESC)

Minimum PHP Version

安装

您可以使用 Composer 下载并安装该函数及其依赖项。

只需将 techworker/php-fn-sortby 依赖项添加到您项目的 composer.json 文件中。以下是一个定义对 php-fn-sortby 版本 1.* 的依赖项的最小 composer.json 示例文件。

{
    "require": {
        "techworker/php-fn-sortby": "~1.0"
    }
}

我想您应该知道的一个小缺点,但我会说 OpCache 会解决这个问题。

该库使用 composer 的 autoload->files 来使函数对您可用。在 PHP 中,函数的自动加载不起作用。因此,无论是否使用,文件总是被加载。

当然,您可以下载库并按需 require 它。

定义和用法

首先导入该函数,以便您可以轻松使用或别名它。

// import
use function techworker\fn\sortBy;

// usage
sortBy($criteria1)->thenBy($criteria2)->thenBy($criteria3);

对您可见的唯一函数是 sortBy。其他一切都是动态创建的函数(或更好的是,实现 techworker\fn\ThenByInterface 的对象),您可以使用与 sortBy 相同的方式使用它们。

定义看起来像这样

sortBy(int|string|callable $comparator[, int $direction = \SORT_ASC[, callable $decorator = null]]) : ThenByInterface

  • string | int | callable $criteria 这可以是字符串或返回排序值的可调用函数。

  • int $direction 排序的方向。这可以是 PHP 中预定义的 SORT_* 常量之一: \SORT_ASC AND \SORT_DESC

  • callable $decorator 可以根据您的 $criteria 应用自定义排序逻辑的装饰器。内置装饰器应该足够强大,可以满足 99% 的情况,因此忽略它将带来良好的结果。如果您需要另一个装饰器,请了解装饰器的作用和使用方法。我们将在文档中省略这一点。

使用字符串作为排序标准

假设我们有一个城市数组,想要按国家升序排序,然后按人口降序排序。

$cities = [
    ['name' => 'Shanghai', 'population' => 24256800, 'country' => 'China'],
    ['name' => 'Karachi',  'population' => 23500000, 'country' => 'Pakistan'],
    ['name' => 'Beijing',  'population' => 21516000, 'country' => 'China']
];

这是最简单的用例,我们可以这样做

use function techworker\fn\sortBy;

// create a sort callback used for the builtin PHP usort function.
$sorter = sortBy('name')->thenBy('population', \SORT_DESC);
usort($cities, $sorter);

现在城市数组已排序。您甚至可以使用对象数组。库将尝试通过数组键 [$criteria] 或对象属性 ->{$criteria} 确定值。

使用回调作为排序标准

如果您需要更深入地检索排序标准,可以提供一个函数。

假设您有一个包含部门及其员工的平均工资的部门数组,嵌套如下

$departments = [
    'devops' => [
        'employees' => ['ben', 'peter', 'james', 'lisa', 'tequila'], // 5
        'salary' => 4000
    ],
    'sysop' => [
        'employees' => ['titus', 'raffy', 'ramanda', 'kathleen', 'rufus'], // 5
        'salary' => 3000
    ],
    'mgmt' => [
        'employees' => ['zoey', 'tiny', 'raphael', 'christian', 'michael', 'rene', 'benny', 
                        'johannes', 'sebastian', 'pedro', 'christoph'], // 11
        'salary' => 10000
    }
];

现在您想按员工数量降序排序,然后按工资降序排序。预期的结果应该如下所示

  • mgmt -> 11 员工,10000 薪资
  • devop -> 5 员工,4000 薪资
  • sysop -> 5 员工,3000 薪资

这是它的工作方式

use function techworker\fn\sortBy;

// I declare the callback in variables for better readability
$fnNumberEmployees = function($department) {
    return count($department['employees']);
}

// create a sort callback used for the builtin PHP usort function.
$sorter = sortBy($fnNumberEmployees, \SORT_DESC)->thenBy('salary', \SORT_DESC);
usort($departments, $sorter);

如您所知,提供给 usort 的比较函数期望两个参数,并返回一个整数来告诉排序器值是 bigger (>0),lower (<0) 还是 equal (0)。

您使用单个参数封装回调函数的操作由装饰器完成(在初始 sortBy 调用的第三个参数)。

因此,如果您提供了一个仅有一个参数的回调函数(一元),装饰器会自动将其封装,并使用关系运算符 <=> 帮助您完成比较。

如果您提供了两个参数的回调函数,您可以自行进行比较。

以下是一个使用两个参数的回调函数的示例

use function techworker\fn\sortBy;

// I declare the callback in variables for better readability
$fnNumberEmployees = function($department1, $department2) {
    return count($department1['employees']) - count($department2['employees']);
    // or 
    // return count($department1['employees']) <=> count($department2['employees']);
}

// create a sort callback used for the builtin PHP usort function.
$sorter = sortBy($fnNumberEmployees, \SORT_DESC)->thenBy('salary', \SORT_DESC);
usort($departments, $sorter);

这应该会给出与上述相同的结果,但您将获得更多关于比较功能本身的控制。

ThenByInterface

sortBythenBy 函数都返回一个 ThenByInterface 的动态类实例,这使得 IDE 在自动完成调用时更为方便。

请查看实现代码 src,与其他各种解决方案相比,它相当简单。但它看起来很复杂。如果您有任何错误、建议等,请通过 GitHub 的问题与我联系。

希望这对你有所帮助!

致谢

此函数大致移植自 https://github.com/Teun/thenBy.js - 一个具有更多或更少相同功能的 JavaScript 函数。非常感谢这个想法!

移植和增强它的关键问题如下

Teun/thenBy.js#10 :-)