JavaScript对象数组如何按指定属性和排序方向进行排序


Posted in Javascript onJune 15, 2016

引子

在以数据为中心的信息系统中,以表格形式展示数据是在常见不过的方式了。对数据进行排序是必不可少的功能。排序可以分为按单个字段排序和按多个字段不同排序方向排序。单字段排序局限性较大,不能满足用户对数据的关注点变化的需求,而多字段排序就可以较好的弥补这个缺陷。

多字段排序,实现的方式从大的层面上可以分为后端实现和前端实现。

后端排序

后端实现排序可以在数据库层面实现或者在应用程序层面实现。

数据库层面实现多字段排序非常简单,使用SQL的排序指令“Order By”即可——Order By field1 asc, field2 desc, field3 asc -- ...。
应用程序层面是指Web应用层(这里不讨论C/S架构),比如PHP、Java Web、ASP.NET等。应用程序层面实现就是使用PHP、Java、.NET(C#/VB)这些后端服务语言来实现对数据的排序。以ASP.NET C# 为例,因为C#中的LINQ内置了对集合类型的诸多操作,并且支持多属性排序,所以使用LINQ能够很方便的实现此目的——from f in foos orderby f.Name descending, f.Num ascending select f(可以发现LINQ的排序语法几乎与SQL的一模一样)。如果其它语言没有内置类似的支持,则按照排序算法来实现,这是通用的,与编程语言无关。

前端排序

在JavaScript中,数组有一个排序方法“sort”,当数组是一个简单数组(数组元素是简单类型——字符串、数值和布尔)时,使用该方法可以很方便的到达排序目的。但是当数组元素是非简单类型,比如名/值对的Object,并且想要按照指定的某几个属性按不同的排序方向进行排序时,简单的调用“sort”方法就不能实现此目的了。

不过好在“sort”方法预留了自定义排序的接口,可以实现想要的排序方式。

来看看数组的“sort”方法是怎样的。

sort函数原型

// 对数组的元素做原地的排序,并返回这个数组。
// 默认按照字符串的Unicode码位点(code point)排序。
Array.prototype.sort([compareFunction]:number); // number:-1 | 0 | 1。
// 典型的比较函数(升序排序)。
function compareFunction(item1, item2) {
if(item1 > item2) {
return 1; // 如果是降序排序,返回-1。
}else if(item1 === item2) {
return 0;
}else {
return -1; // 如果是降序排序,返回1。
}
}

说明:如果没有指明compareFunction,那么元素会被转换为字符串的诸个字符并按照Unicode位点顺序排序。例如,"Cherry"会被排列到"banana"之前。当对数字进行排序的时候, 9 会出现在 80 之前,因为他们会先被转换为字符串,而 "80" 比 "9" 要靠前。

•如果 compareFunction(a, b) 小于 0 ,那么 a 会被排列到 b 之前;

•如果 compareFunction(a, b) 等于 0 ,a 和 b

的相对位置不变。备注:ECMAScript标准并不保证这一行为,而且也不是所有浏览器都会遵守(例如 Mozilla 在 2003
年之前的版本);

•如果 compareFunction(a, b) 大于 0 , b 会被排列到 a 之前。

•compareFunction(a, b) 必须总是对相同的输入返回相同的比较结果,否则排序的结果将是不确定的。

注:以上规则得出的排序结果是升序的,如果想要得到降序的结果,则在比较结果大于 0 时返回小于 0 的结果,比较结果小于 0 时 返回大于 0 的结果即可。

要实现多属性排序,关键就在于比较函数的实现。根据以上规则, 实现多属性不同方向排序,依然要返回两个比较项的大小关系。

那么多属性对象的大小关系如何确定呢?这个可以分两步走。

第一步,记录下两个排序项按照各个排序属性及方向进行比较得到的结果。

var propOrders = { "prop1":"asc", "prop2":"desc", "prop3":"asc"};
function cmp(item1, item2, propOrders) {
var cps = []; // 用于记录各个排序属性的比较结果,-1 | 0 | 1 。
var isAsc = true; // 排序方向。 
for(var p in propOrders) {
isAsc = propOrders[p] === "asc";
if(item1[p] > item2[p]) {
cps.push(isAsc ? 1 : -1);
break; // 可以跳出循环了,因为这里就已经知道 item1 “大于” item2 了。
} else if(item1[p] === item2[p]) {
cps.push(0);
} else {
cps.push(isAsc ? -1 : 1);
break; // 可以跳出循环,item1 “小于” item2。
} 
} 
/*
.
.
.
*/
}

第二步,根据各排序属性比较结果综合判断得出两个比较项的最终大小关系。

/* 
.
.
. 
*/
for(var j = 0; j < cps.length; j++) {
if(cps[j] === 1 || cps[j] === -1) {
return cps[j];
}
}
return 0;

有了上述思路后,实现整个比较函数就容易了,下面是比较函数的完整JavaScript代码:

比较函数

function SortByProps(item1, item2) {
"use strict";
var props = [];
for (var _i = 2; _i < arguments.length; _i++) {
props[_i - 2] = arguments[_i];
}
var cps = []; // 存储排序属性比较结果。
// 如果未指定排序属性,则按照全属性升序排序。 
var asc = true;
if (props.length < 1) {
for (var p in item1) {
if (item1[p] > item2[p]) {
cps.push(1);
break; // 大于时跳出循环。
} else if (item1[p] === item2[p]) {
cps.push(0);
} else {
cps.push(-1);
break; // 小于时跳出循环。
}
}
} else {
for (var i = 0; i < props.length; i++) {
var prop = props[i];
for (var o in prop) {
asc = prop[o] === "asc";
if (item1[o] > item2[o]) {
cps.push(asc ? 1 : -1);
break; // 大于时跳出循环。
} else if (item1[o] === item2[o]) {
cps.push(0);
} else {
cps.push(asc ? -1 : 1);
break; // 小于时跳出循环。
}
}
}
} 
for (var j = 0; j < cps.length; j++) {
if (cps[j] === 1 || cps[j] === -1) {
return cps[j];
}
}
return 0; 
}

测试用例

// -------------测试用例------------------------------
var items = [ { name: 'Edward', value: 21 },
{ name: 'Sharpe', value: 37 },
{ name: 'And', value: 45 },
{ name: 'Edward', value: -12 },
{ name: 'Magnetic', value: 21 },
{ name: 'Zeros', value: 37 }
];
function test(propOrders) {
items.sort(function (a, b) {
return SortByProps(a, b, propOrders);
});
console.log(items);
}
function testAsc() {
test({ "name": "asc", "value": "asc" });
}
function testDesc() {
test({ "name": "desc", "value": "desc" });
}
function testAscDesc() {
test({ "name": "asc", "value": "desc" });
}
function testDescAsc() {
test({ "name": "desc", "value": "asc" });
} 
TypeScript代码
/**
** 排序方向。
*/
type Direct = "asc" | "desc";
/**
** 排序属性。
** 
** @interface IPropertyOrder
*/
interface IPropertyOrder { 
[name: string] : Direct;
}
/**
** 简单名/值对象。
** 
** @interface ISimpleObject
*/
interface ISimpleObject {
[name: string] : string | number | boolean;
}
/**
** 对简单的名/值对象按照指定属性和排序方向进行排序(根据排序属性及排序方向,
** 对两个项依次进行比较,并返回代表排序位置的值)。
** 
** @template T 简单的名/值对象。
** @param {T} item1 排序比较项1。
** @param {T} item2 排序比较项2。
** @param {...IPropertyOrder[]} props 排序属性。
** @returns 若项1大于项2返回1,若项1等于项2返回0,否则返回-1。
*/
function SortByProps<T extends ISimpleObject>
(item1: T, item2: T, ...props: IPropertyOrder[]) {
"use strict";
var cps: Array<number> = []; // 存储排序属性比较结果。
// 如果未指定排序属性,则按照全属性升序排序。 
var asc = true;
if (props.length < 1) {
for (var p in item1) {
if (item1[p] > item2[p]) {
cps.push(1);
break; // 大于时跳出循环。
} else if (item1[p] === item2[p]) {
cps.push(0);
} else {
cps.push(-1);
break; // 小于时跳出循环。
}
}
} else { // 按照指定属性及升降方向进行排序。
for (var i = 0; i < props.length; i++) {
var prop = props[i];
for (var o in prop) {
asc = prop[o] === "asc";
if (item1[o] > item2[o]) {
cps.push(asc ? 1 : -1);
break; // 大于时跳出循环。
} else if (item1[o] === item2[o]) {
cps.push(0);
} else {
cps.push(asc ? -1 : 1);
break; // 小于时跳出循环。
}
}
}
}
for (var j = 0; j < cps.length; j++) {
if (cps[j] === 1 || cps[j] === -1) {
return cps[j];
}
}
return 0; 
}

使用场景及局限性

在前端使用JavaScript实现多属性排序,减少了对服务器端的请求,减轻服务器端的计算压力,但是也仅适用于只需要对本地数据进行排序的情形。如果需要对整个数据集进行多属性排序,最终还是要在服务器端的数据库层面上进行。

以上所述是小编给大家介绍的JavaScript对象数组如何按指定属性和排序方向进行排序的全部叙述,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Javascript 相关文章推荐
jQuery 研究心得 取得属性的值
Nov 30 Javascript
解决bootstrap中modal遇到Esc键无法关闭页面
Mar 09 Javascript
JavaScript函数节流概念与用法实例详解
Jun 20 Javascript
JS+Canvas实现的俄罗斯方块游戏完整实例
Dec 12 Javascript
Three.js基础学习之场景对象
Sep 27 Javascript
使用puppeteer破解极验的滑动验证码
Feb 24 Javascript
详解如何在微信小程序开发中正确的使用vant ui组件
Sep 13 Javascript
vue+element实现表格新增、编辑、删除功能
May 28 Javascript
jquery操作checkbox的常用方法总结【附测试源码下载】
Jun 10 jQuery
vue实现移动端省市区选择
Sep 27 Javascript
jQuery轮播图功能制作方法详解
Dec 03 jQuery
vscode调试node.js的实现方法
Mar 22 Javascript
jQuery动态加载css文件实现方法
Jun 15 #Javascript
异步加载JS、CSS代码(推荐)
Jun 15 #Javascript
全面解析Javascript无限添加QQ好友原理
Jun 15 #Javascript
漫谈JS引擎的运行机制 你应该知道什么
Jun 15 #Javascript
JavaScript操作 url 中 search 部分方法函数
Jun 15 #Javascript
JS实现动态表格的添加,修改,删除功能(推荐)
Jun 15 #Javascript
JS封装的自动创建表格的实现代码
Jun 15 #Javascript
You might like
PHP实现的功能是显示8条基色色带
2006/10/09 PHP
用PHP实现ODBC数据分页显示一例
2006/10/09 PHP
Linux安装配置php环境的方法
2016/01/14 PHP
php使用crypt()函数进行加密
2017/06/08 PHP
从盛大通行证上摘下来的身份证验证js代码
2011/01/11 Javascript
jquery监控数据是否变化(修正版)
2011/04/12 Javascript
js和html5实现手机端刮刮卡抽奖效果完美兼容android/IOS
2013/11/18 Javascript
用jquery的方法制作一个简单的导航栏
2014/06/23 Javascript
JavaScript 2048 游戏实例代码(简单易懂)
2016/03/25 Javascript
基于node.js实现微信支付退款功能
2017/12/19 Javascript
vue项目刷新当前页面的三种方法
2018/12/04 Javascript
webpack结合express实现自动刷新的方法
2019/05/07 Javascript
react koa rematch 如何打造一套服务端渲染架子
2019/06/26 Javascript
微信小程序 数据缓存实现方法详解
2019/08/26 Javascript
JavaScript实现缓动动画
2020/11/25 Javascript
[01:34]2014DOTA2 TI预选赛预选赛 选手比赛房大揭秘!
2014/05/20 DOTA
[01:35]2018完美盛典章节片——共竞
2018/12/17 DOTA
举例详解Python中threading模块的几个常用方法
2015/06/18 Python
解决python大批量读写.doc文件的问题
2018/05/08 Python
python中int与str互转方法
2018/07/02 Python
python读取Excel实例详解
2018/08/17 Python
PyQt5 对图片进行缩放的实例
2019/06/18 Python
pytorch 归一化与反归一化实例
2019/12/31 Python
Django+Celery实现动态配置定时任务的方法示例
2020/05/26 Python
keras的load_model实现加载含有参数的自定义模型
2020/06/22 Python
python中数字是否为可变类型
2020/07/08 Python
CSS3动画之利用requestAnimationFrame触发重新播放功能
2019/09/11 HTML / CSS
师范大学毕业自我鉴定
2013/11/21 职场文书
适用于所有创业者的创业计划书
2014/02/05 职场文书
公司担保书格式范文
2014/05/12 职场文书
完美的中文自荐信
2014/05/24 职场文书
优质服务口号
2014/06/11 职场文书
新闻发布会活动策划方案
2014/09/15 职场文书
爱牙日宣传活动总结
2015/02/05 职场文书
2015年小学语文教师工作总结
2015/10/23 职场文书
python文件目录操作之os模块
2021/05/08 Python