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 相关文章推荐
网上抓的一个特效
May 11 Javascript
js遍历td tr等html元素
Dec 13 Javascript
理解Javascript闭包
Nov 01 Javascript
JS获得浏览器版本和操作系统版本的例子
May 13 Javascript
javascript实现base64 md5 sha1 密码加密
Sep 09 Javascript
JS实现把鼠标放到链接上出现滚动文字的方法
Apr 06 Javascript
jQuery.Validate表单验证插件的使用示例详解
Jan 04 Javascript
ES6新特性之模块Module用法详解
Apr 01 Javascript
JS中Swiper的使用和轮播图效果
Aug 11 Javascript
JS实现将对象转化为数组的方法分析
Jan 21 Javascript
jQuery实现中奖播报功能(让文本滚动起来) 简单设置数值即可
Mar 20 jQuery
vue基于Teleport实现Modal组件
May 31 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
Discuz 6.0+ 批量注册用户名
2009/09/13 PHP
让你成为更出色的PHP开发者的10个技巧
2011/02/25 PHP
php session安全问题分析
2011/06/24 PHP
PHP实现获取中英文首字母
2015/06/19 PHP
Yii2框架自定义验证规则操作示例
2019/02/08 PHP
解决laravel5.4下的group by报错的问题
2019/10/16 PHP
JavaScript 面向对象编程(2) 定义类
2010/05/18 Javascript
jquery.jstree 增加节点的双击事件代码
2010/07/27 Javascript
jquery实现点击TreeView文本父节点展开/折叠子节点
2013/01/10 Javascript
javascript中使用正则计算中文长度的例子
2014/04/29 Javascript
自定义jQuery插件方式实现强制对象重绘的方法
2015/03/23 Javascript
jQuery判断checkbox选中状态
2016/05/12 Javascript
nodejs的HTML分析利器node-jquery用法浅析
2016/11/08 NodeJs
JavaScript制作简易计算器(不用eval)
2017/02/05 Javascript
jQuery实现简单弹窗遮罩效果
2017/02/27 Javascript
JS实现点击Radio动态更新table数据
2017/07/18 Javascript
微信页面弹出键盘后iframe内容变空白的解决方案
2017/09/20 Javascript
vue webpack打包优化操作技巧
2018/02/22 Javascript
原生JS实现前端本地文件上传
2018/09/08 Javascript
vue配置接口域名方法总结
2019/05/12 Javascript
Vue前端项目部署IIS的实现
2020/01/06 Javascript
electron 如何将任意资源打包的方法步骤
2020/04/16 Javascript
深入解读VUE中的异步渲染的实现
2020/06/19 Javascript
微信小程序实现日历小功能
2020/11/18 Javascript
[02:57]DOTA2英雄基础教程 风行者
2014/01/16 DOTA
python使用thrift教程的方法示例
2019/03/21 Python
python读出当前时间精度到秒的代码
2019/07/05 Python
Django之form组件自动校验数据实现
2020/01/14 Python
Python自定义sorted排序实现方法详解
2020/09/18 Python
HTML5引入的新数组TypedArray介绍
2012/12/24 HTML / CSS
韩国11街:11STREET
2018/03/27 全球购物
大学系主任推荐信范文
2013/12/24 职场文书
党员民主生活会对照检查材料思想汇报
2014/09/28 职场文书
党员群众路线个人整改措施思想汇报
2014/10/12 职场文书
雷锋的观后感
2015/06/10 职场文书
个人催款函范文
2015/06/24 职场文书