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 相关文章推荐
复制Input内容的js代码_支持所有浏览器,修正了Firefox3.5以上的问题
Jun 21 Javascript
javascript中的prototype属性使用说明(函数功能扩展)
Aug 16 Javascript
ExtJs Excel导出并下载IIS服务器端遇到的问题
Sep 16 Javascript
AngularJS入门教程之AngularJS模型
Apr 18 Javascript
JS实现的跨浏览器解析XML文件实例
Jun 21 Javascript
基于JS如何实现给字符加千分符(65,541,694,158)
Aug 03 Javascript
关于jquery中动态增加select,事件无效的快速解决方法
Aug 29 Javascript
快速解决js中window.location.href不工作的问题
Nov 02 Javascript
vuex的module模块用法示例
Nov 12 Javascript
基于Three.js实现360度全景图片
Dec 30 Javascript
vue实践---根据不同环境,自动转换请求的url地址操作
Sep 21 Javascript
详解Vue的七种传值方式
Feb 08 Vue.js
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
joomla数据库操作示例代码
2016/01/06 PHP
PHP模板引擎Smarty内建函数section,sectionelse用法详解
2016/04/11 PHP
tp5(thinkPHP5)操作mongoDB数据库的方法
2018/01/20 PHP
PHP实现数组向任意位置插入,删除,替换数据操作示例
2019/04/05 PHP
PHP进阶学习之类的自动加载机制原理分析
2019/06/18 PHP
什么是json和jsonp,jQuery json实例详详细说明
2012/12/11 Javascript
javascript类型转换使用方法
2014/02/08 Javascript
解决用jquery load加载页面到div时,不执行页面js的问题
2014/02/22 Javascript
jQuery插件开发精品教程让你的jQuery提升一个台阶
2016/01/27 Javascript
AngularJS中的$watch(),$digest()和$apply()区分
2016/04/04 Javascript
AngulaJS路由 ui-router 传参实例
2017/04/28 Javascript
JS判断数组那点事
2017/10/10 Javascript
详解react-refetch的使用小例子
2019/02/15 Javascript
Python中optparse模块使用浅析
2015/01/01 Python
Python中用format函数格式化字符串的用法
2015/04/08 Python
深入讲解Python中面向对象编程的相关知识
2015/05/25 Python
Python中的time模块与datetime模块用法总结
2016/06/30 Python
Pytorch 实现权重初始化
2019/12/31 Python
python使用python-pptx删除ppt某页实例
2020/02/14 Python
如何解决python多种版本冲突问题
2020/10/13 Python
手把手教你用纯css3实现轮播图效果实例
2017/05/04 HTML / CSS
Under Armour安德玛德国官网:美国高端运动科技品牌
2019/03/09 全球购物
澳大利亚个性化儿童礼品网站:Bright Star Kids
2019/06/14 全球购物
What's the difference between an interface and abstract class? (接口与抽象类有什么区别)
2012/10/29 面试题
小班重阳节活动方案
2014/02/08 职场文书
成龙霸王洗发水广告词
2014/03/14 职场文书
《凡卡》教学反思
2014/04/09 职场文书
乡镇挂职心得体会
2014/09/04 职场文书
大二学生学年自我鉴定
2014/09/12 职场文书
学习十八大演讲稿
2014/09/15 职场文书
公司更名通知函
2015/04/24 职场文书
感恩的心主题班会
2015/08/12 职场文书
高考百日冲刺决心书
2015/09/23 职场文书
2016年中秋祝酒词
2015/11/26 职场文书
导游词之新疆-喀纳斯
2019/10/10 职场文书
JS函数式编程实现XDM一
2022/06/16 Javascript