最近在写个人博客网站,遇到了一个数组嵌套对象排序的问题,在解决过程中遇到了一些坑。在此详细说一下,以免后来人踩坑!
需求描述
现有简化后的数据内容如下:
let data = [
{ArtId: 82, ArtName: "关于JavaScript处理时间戳转日期字符串与日期字符串转时间戳的函数", Date: 1630937933, State: 1,},
{ArtId: 83, ArtName: "在manjaro下安装安卓投屏软件scrcpy详细过程", Date: 1630937929, State: 1},
{ArtId: 84, ArtName: "学习GoFrame框架,从头开始一步步搭建个人博客WEB应用(中)", Date: 1630937925, State: 1},
{ArtId: 80, ArtName: "关于JavaScript存、取Cookie的函数", Date: 1630937921, State: 1}
]
现在需求是点击“标题”按钮,那么将data按ArtName正向排序,再点击一次反向排序,再点一次还原到原来的顺序。
另外3个字段也是如此:点击“日期”按钮,按Date排序;点击“状态”按钮,按State排序;点击“ID”按钮,按“ArtId”排序。
分析需求
这里需要用到数组的sort方法。sort的调用方式是data.sort(function(key))。
这个funtion必须有一个参数,它必须是data数组中的键;该函数会根据参数key提取sort函数传进来的2个元素的相关值:2个元素比较后返回正数那么做升序处理;返回0那么位置不变;返回负数那么做降序处理。
现在需要写一个可以同时比较数字、字符串以及异常情况的比较函数。
解决过程
首先写一个只比较数字的函数,这个很简单,内容如下:
function compare(key) {
return function (a, b) {
let c = a[key]-b[key];
return c > 0 ? 1 : c === 0 ? 0 : -1;
}
}
这是一个闭包函数,key是数组中对象的键,a和b代表的是sort传进来的2个数组元素。
测试比较函数
这个简单,按f12打开控制台,把let data那段贴进去回车,然后把function compare贴进去回车,再执行data.sort(compare("Date"));
结果如下:
增加sort反向排序功能
那么如何只用sort达到既可正向排序又可反向排序呢??
请看下面的代码:
function compare(key,flag=1) {
flag = flag === 1 ? 1 : -1;
return function (a, b) {
let c = a[key]-b[key];
return flag*(c > 0 ? 1 : c === 0 ? 0 : -1);
}
}
加一个标志位参数,若没传第二个参数,那么是正向排序,如果传了第二个参数(无论第二个参数是啥)那么就反向排序。
至于为啥写flag = flag === 1 ? 1 : -1;
这句代码,这是防止传参不合规。
最终代码
前面的compare只能处理数字,不能处理字符串、没有对异常做处理。接下来要对异常做处理,还要对字符串做处理:
// 排序函数,可处理数字、undefined、NaN
function compare(key, flag = 1) {
flag = flag === 1 ? 1 : -1;
return function (a, b) {
if (typeof a[key] === "number" && typeof b[key] === "number") { // 比较双方都是数字,那么根据数字大小作比较
let c = a[key] - b[key];
return flag * (c > 0 ? 1 : c === 0 ? 0 : -1);
}
if (typeof a[key] === "undefined" || Number.isNaN(a[key])) { // 判断a是未定义或NaN么?
if (typeof b[key] === "undefined" || Number.isNaN(b[key])) { // 判断b也是未定义或NaN么?
return 0; // a和b都是异常值,那么返回0
} else {
return -flag; // a异常,b不异常时,当成a小于b处理
}
}
if (typeof b[key] === "undefined" || Number.isNaN(b[key])) {
return flag; // b异常,a不异常时,当成a大于b处理
}
a = a[key].toString(); // a既不是数字、也不是异常,那么转成字符串进行后面的比较
b = b[key].toString(); // b既不是数字、也不是异常,那么转成字符串进行后面的比较
return flag * (a > b ? 1 : a === b ? 0 : -1); // 两个字符串可以直接比较大小
}
}
代码测试
在测试台执行对数组不同字段进行排序的测试:
data.sort(compare("Date")); // 对数组按"Date"字段进行从小到大排序
data.sort(compare("Date",1)); // 对数组按"Date"字段进行从大到小排序
data.sort(compare("Date")); // 对数组按"Date"字段进行从小到大排序
data.sort(compare("Date",1)); // 对数组按"Date"字段进行从大到小排序
data.sort(compare("Date")); // 对数组按"Date"字段进行从小到大排序
data.sort(compare("Date",1)); // 对数组按"Date"字段进行从大到小排序
data.sort(compare("ArtName")); // 对数组按"Date"字段进行从小到大排序
data.sort(compare("ArtName",1)); // 对数组按"Date"字段进行从大到小排序
data.sort(compare("ArtName")); // 对数组按"Date"字段进行从小到大排序
data.sort(compare("ArtName",1)); // 对数组按"Date"字段进行从大到小排序
data.sort(compare("ArtName")); // 对数组按"Date"字段进行从小到大排序
data.sort(compare("ArtName",1)); // 对数组按"Date"字段进行从大到小排序
写完代码一定要做非常细致的测试,确保不出bug。可以按上面的测试指令对数组不同的字段进行正向排序和反向排序比较,多次执行,检查结果是否都符合预期。
以上代码经测试可以满足对数组的数字类型字段、字符串类型字段、undefined类型字段、NaN字段类型进行比较,其他数据类型会转成字符串型进行比较。