2016 年 12 月 23 日星期五

使用绝对定位数据进行排序

DataTable 中的信息排序很可能是我在本博客中讨论得最多的内容(例如,枚举日期,还有更多),但这样做是因为这是一个非常丰富的主题!DataTables 在广泛的应用程序中使用,从太空望远镜遥测到业余足球联赛,因此它需要能够应对众多不同的数据类型。DataTables 核心仅附带一组基本数据排序函数(字符串、数字、货币、百分比、ISO8601 日期),因此,对于超出此范围内的任何内容,DataTables 提供了 排序插件 API,您可以使用它来定义自己的排序方法。

通常,对于排序方法,您会使用线性排序,其中降序排序是升序排序的确切逆序,并且所有内容都按简单比较(数字或字符代码点)排序。本文将略有不同:我将介绍一个非线性可配置的排序插件,您可以使用它将特定数据保留在列的顶部或底部,无论排序方向和该项的值如何。

我们以完全相反的方式完成此文章!我们从一个完成的示例开始,然后描述 API,如果只想使用它而不考虑任何实现细节,最后,如果您对更多技术方面感兴趣(当然您感兴趣 - 这是一个开发者博客!),我将讨论实现。

示例

一个示例通常价值百万字,因此让我们考虑以下表格

姓名 职位 办公室 开始日期 薪资
Tiger Nixon 系统架构师 爱丁堡 2011/04/25 $320,800
Garrett Winters 会计 东京 2011/07/25 $170,750
Ashton Cox 初级技术作者 旧金山 2009/01/12 $86,000
未知 高级 Javascript 开发人员 爱丁堡 2012/03/29 待定
Airi Satou 会计 东京 2008/11/28 $162,700
Brielle Williamson 集成专家 纽约 2012/12/02 $372,000
未知 销售助理 旧金山 2012/08/06 待定
Rhona Davidson 集成专家 东京 2010/10/14 $327,900
Colleen Hurst Javascript 开发人员 旧金山 2009/09/15 不可用
Sonya Frost 软件工程师 爱丁堡 2008/12/13 $103,600

在这种情况下,我们有以下两列可用绝对排序

  • 姓名列中有 “未知”条目,应显示在表格顶部。
  • 薪资列中 “待定” 应显示在顶部,“不可用” 应显示在底部。

如果您通过单击这些列的标题来更改应用于表格的排序,您将能够看到这些项无论列的排序如何,都会保持原位。如果对其他列之一应用排序,则行将按正常方式排序。

为什么

可以运行,但为什么想要这样做呢?主要目的是突出显示某些数据,例如需要填入的缺失数据(使用 编辑器,这是文章中唯一的推销宣传!)。还有一些数据可能会被降级为上文显示的“不可用”薪资,这样也可能非常有用。

如何使用

首先需要在页面上包含插件来源。数据表 CDN 上提供了这里

JS

为了让每个数据集都各不相同,需要对插件进行配置,以告知它在表格顶部和/或底部保留哪些内容。这是通过两个函数完成的

  • DataTable.absoluteOrder 用于对字符串排序
  • DataTable.absoluteOrderNumber 用于对数字排序。

这两个方法都接受一个参数,该参数可以是

  • 字符串 - 在一列中应保持在表格顶部的值。
  • 对象 - 具有以下属性的对象
    • - 保留在表格顶部或底部的值
    • 位置 - 该数据在排序顺序中应保持的位置,顶部底部
  • 数组 - 匹配上述描述的字符串或对象数组,以便可以在排序中特殊处理多个值。

该函数将构建并附加一个排序插件,用于数据表,该插件将执行所需的排序。但是,一个重要之处在于,该插件并不进行自动类型检测。相反,它将返回一个值,该值应分配给 columns.type 选项,用于执行这种排序的列。这是一个关键点,在此博客中公布的大多数其他插件将执行自动类型检测,但是由于其动态特性和可能不希望将其应用于匹配使用值的列这一事实,因此无法可靠且高效地检测适用于此插件的列。

示例用法

最简单的用例是在列顶部对特定数据片进行排序

var nameType = DataTable.absoluteOrder( 'Unknown' );

$('#myTable').DataTable( {
    columnDefs: [
        { targets: 0, type: nameType }
    ]
} );

如果希望项目位于表格底部

var nameType = DataTable.absoluteOrder( {
    value: 'Unknown', position: 'bottom'
} );

$('#myTable').DataTable( {
    columnDefs: [
        { targets: 0, type: nameType }
    ]
} );

示例代码

用于上述示例的代码结合使用了配置的这三个选项;由于薪资放在列的顶部和底部,因此使用数组。如下所示

var nameType = DataTable.absoluteOrder( 'Unknown' );
var salaryType = DataTable.absoluteOrderNumber( [
    'TBC',
    { value: 'Not available', position: 'bottom' }
] );

$('#myTable').DataTable( {
    columnDefs: [
        {
            targets: 0,
            type: nameType
        },
        {
            targets: 5,
            type: salaryType
        }
    ]
} );

这就是全部内容!

其工作原理

为 DataTables 制作排序插件其实很容易;这不是此软件的有趣之处,但我们了解在 JavaScript 中排序是如何工作的非常关键,特别是我们如何将自定义函数传递给 Array.prototype.sort(),以定义我们希望如何对数据进行排序。与往常一样,非常棒的 MDN 对此有精辟描述

  • 如果 compareFunction(a, b) 小于 0,则对 a 排序比 b 的索引低,即 a 优先。
  • 如果 compareFunction(a, b) 返回 0,则让 a 和 b 相对于彼此保持不变,但相对于所有不同元素进行排序。
  • 如果 compareFunction(a, b) 大于 0,则对 b 排序比 a 的索引低。

因此我们需要做的事情是确定传递给排序函数的任一参数是否需要位于排序数组的顶部或底部。可以通过简单的 for 循环进行操作,但这是一种针对该问题的 O(n) 解决方案,在排序比较函数中,我们希望事情尽可能快,因为其执行频率很高,特别是对于较大的数据集。为了使其 O(1),我们可以准备一个对象,该对象包含作为参数名称要搜索的值,然后可以使用简单的查找对其进行检查。例如,假设我们有 var o = { "Unknown": true },若要检查对象中是否存在一个值,我们只需使用 if ( o[value] ) {...}

因此,为了构建所需对象,我们可以使用

var alwaysTop = {};
var alwaysBottom = {};

for ( var i=0, ien=values.length ; i<ien ; i++ ) {
    var conf = values[i];

    if ( typeof conf === 'string' ) {
        alwaysTop[ conf ] = true;
    }
    else if ( conf.position === undefined || conf.position === 'top' ) {
        alwaysTop[ conf.value ] = true;
    }
    else {
        alwaysBottom[ conf.value ] = true;
    }
}

然后我们的排序函数可以简单地包含

function ( a, b ) {
    if ( o.alwaysTop[ a ] || o.alwaysBottom[ b ] ) {
        return -1;
    }
    else if ( o.alwaysBottom[ a ] || o.alwaysTop[ b ] ) {
        return 1;
    }

    return ((a < b) ? -1 : ((a > b) ? 1 : 0));
};

此插件中的所有其他内容也只是打包,以便允许基于数字的排序、UMD 加载,并使代码大小相对较小。

可改进之处

总有一种方法可以改善某些事情,如果你对此插件有任何建议或投稿,请随意发送请求!源代码在 GitHub 上提供。或者,如果你有任何关于自己排序插件的想法,我们将非常欢迎。

节日快乐,各位!