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 相关文章推荐
JavaScript与Image加载事件(onload)、加载状态(complete)
Feb 14 Javascript
THREE.JS入门教程(4)创建粒子系统
Jan 24 Javascript
jQuery prototype冲突的2种解决方法(附demo示例下载)
Jan 21 Javascript
JS中传递参数的几种不同方法比较
Jan 20 Javascript
jQuery操作之效果详解
May 19 jQuery
angular动态删除ng-repaeat添加的dom节点的方法
Jul 20 Javascript
JS生成随机打乱数组的方法示例
Dec 23 Javascript
浅析vue深复制
Jan 29 Javascript
前后端如何实现登录token拦截校验详解
Sep 03 Javascript
详解ES6 Symbol 的用途
Oct 14 Javascript
JS实现点击按钮随机生成可拖动的不同颜色块示例
Jan 30 Javascript
仿iPhone通讯录制作小程序自定义选择组件的实现
May 23 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
全国FM电台频率大全 - 19 广东省
2020/03/11 无线电
PHP常用代码大全(新手入门必备)
2010/06/29 PHP
php最简单的删除目录与文件实现方法
2014/11/28 PHP
PHP实现带重试功能的curl连接示例
2016/07/28 PHP
php字符集转换
2017/01/23 PHP
PHP编程快速实现数组去重的方法详解
2017/07/22 PHP
浅谈PHP无限极分类原理
2019/03/14 PHP
Docker 安装 PHP并与Nginx的部署实例讲解
2021/02/27 PHP
javascript globalStorage类代码
2009/06/04 Javascript
获取元素距离浏览器周边的位置的方法getBoundingClientRect
2013/04/17 Javascript
基于jQuery实现拖拽图标到回收站并删除功能
2015/11/25 Javascript
详解WordPress开发中get_current_screen()函数的使用
2016/01/11 Javascript
Jquery给当前页或者跳转后页面的导航栏添加选中后样式的实例
2016/12/08 Javascript
Angular的$http的ajax的请求操作(推荐)
2017/01/10 Javascript
vue利用axios来完成数据的交互
2018/03/23 Javascript
js数组去重的N种方法(小结)
2018/06/07 Javascript
vue实现导航菜单和编辑文本的示例代码
2020/07/04 Javascript
在Python中使用pngquant压缩png图片的教程
2015/04/09 Python
python实现的简单FTP上传下载文件实例
2015/06/30 Python
python numpy 按行归一化的实例
2019/01/21 Python
python中的句柄操作的方法示例
2019/06/20 Python
在django中,关于session的通用设置方法
2019/08/06 Python
python爬虫selenium和phantomJs使用方法解析
2019/08/08 Python
屏蔽Django admin界面添加按钮的操作
2020/03/11 Python
python动态规划算法实例详解
2020/11/22 Python
python subprocess pipe 实时输出日志的操作
2020/12/05 Python
Expedia泰国:预订机票、酒店和旅游包(航班+酒店)
2016/09/27 全球购物
用友笔试题目
2016/10/25 面试题
自荐信要包含哪些内容
2013/11/06 职场文书
一名毕业生的自我鉴定
2013/12/04 职场文书
小学教学随笔感言
2014/02/26 职场文书
读书伴我成长演讲稿
2014/05/07 职场文书
公司授权委托书样本
2014/09/15 职场文书
病假证明模板
2015/06/19 职场文书
幼儿园托班开学寄语(2016秋季)
2015/12/03 职场文书
mysql优化之query_cache_limit参数说明
2021/07/01 MySQL