JavaScript对象数组如何按指定属性和排序方向进行排序
引子
在以数据为中心的信息系统中,以表格形式展示数据是在常见不过的方式了。对数据进行排序是必不可少的功能。排序可以分为按单个字段排序和按多个字段不同排序方向排序。单字段排序局限性较大,不能满足用户对数据的关注点变化的需求,而多字段排序就可以较好的弥补这个缺陷。
多字段排序,实现的方式从大的层面上可以分为后端实现和前端实现。
后端排序
后端实现排序可以在数据库层面实现或者在应用程序层面实现。
数据库层面实现多字段排序非常简单,使用SQL的排序指令“OrderBy”即可——OrderByfield1asc,field2desc,field3asc--...。
应用程序层面是指Web应用层(这里不讨论C/S架构),比如PHP、JavaWeb、ASP.NET等。应用程序层面实现就是使用PHP、Java、.NET(C#/VB)这些后端服务语言来实现对数据的排序。以ASP.NETC#为例,因为C#中的LINQ内置了对集合类型的诸多操作,并且支持多属性排序,所以使用LINQ能够很方便的实现此目的——fromfinfoosorderbyf.Namedescending,f.Numascendingselectf(可以发现LINQ的排序语法几乎与SQL的一模一样)。如果其它语言没有内置类似的支持,则按照排序算法来实现,这是通用的,与编程语言无关。
前端排序
在JavaScript中,数组有一个排序方法“sort”,当数组是一个简单数组(数组元素是简单类型——字符串、数值和布尔)时,使用该方法可以很方便的到达排序目的。但是当数组元素是非简单类型,比如名/值对的Object,并且想要按照指定的某几个属性按不同的排序方向进行排序时,简单的调用“sort”方法就不能实现此目的了。
不过好在“sort”方法预留了自定义排序的接口,可以实现想要的排序方式。
来看看数组的“sort”方法是怎样的。
sort函数原型
//对数组的元素做原地的排序,并返回这个数组。
//默认按照字符串的Unicode码位点(codepoint)排序。
Array.prototype.sort([compareFunction]:number);//number:-1|0|1。
//典型的比较函数(升序排序)。
functioncompareFunction(item1,item2){
if(item1>item2){
return1;//如果是降序排序,返回-1。
}elseif(item1===item2){
return0;
}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的结果即可。
要实现多属性排序,关键就在于比较函数的实现。根据以上规则,实现多属性不同方向排序,依然要返回两个比较项的大小关系。
那么多属性对象的大小关系如何确定呢?这个可以分两步走。
第一步,记录下两个排序项按照各个排序属性及方向进行比较得到的结果。
varpropOrders={"prop1":"asc","prop2":"desc","prop3":"asc"};
functioncmp(item1,item2,propOrders){
varcps=[];//用于记录各个排序属性的比较结果,-1|0|1。
varisAsc=true;//排序方向。
for(varpinpropOrders){
isAsc=propOrders[p]==="asc";
if(item1[p]>item2[p]){
cps.push(isAsc?1:-1);
break;//可以跳出循环了,因为这里就已经知道item1“大于”item2了。
}elseif(item1[p]===item2[p]){
cps.push(0);
}else{
cps.push(isAsc?-1:1);
break;//可以跳出循环,item1“小于”item2。
}
}
/*
.
.
.
*/
}
第二步,根据各排序属性比较结果综合判断得出两个比较项的最终大小关系。
/*
.
.
.
*/
for(varj=0;j<cps.length;j++){
if(cps[j]===1||cps[j]===-1){
returncps[j];
}
}
return0;
有了上述思路后,实现整个比较函数就容易了,下面是比较函数的完整JavaScript代码:
比较函数
functionSortByProps(item1,item2){
"usestrict";
varprops=[];
for(var_i=2;_i<arguments.length;_i++){
props[_i-2]=arguments[_i];
}
varcps=[];//存储排序属性比较结果。
//如果未指定排序属性,则按照全属性升序排序。
varasc=true;
if(props.length<1){
for(varpinitem1){
if(item1[p]>item2[p]){
cps.push(1);
break;//大于时跳出循环。
}elseif(item1[p]===item2[p]){
cps.push(0);
}else{
cps.push(-1);
break;//小于时跳出循环。
}
}
}else{
for(vari=0;i<props.length;i++){
varprop=props[i];
for(varoinprop){
asc=prop[o]==="asc";
if(item1[o]>item2[o]){
cps.push(asc?1:-1);
break;//大于时跳出循环。
}elseif(item1[o]===item2[o]){
cps.push(0);
}else{
cps.push(asc?-1:1);
break;//小于时跳出循环。
}
}
}
}
for(varj=0;j<cps.length;j++){
if(cps[j]===1||cps[j]===-1){
returncps[j];
}
}
return0;
}
测试用例
//-------------测试用例------------------------------
varitems=[{name:'Edward',value:21},
{name:'Sharpe',value:37},
{name:'And',value:45},
{name:'Edward',value:-12},
{name:'Magnetic',value:21},
{name:'Zeros',value:37}
];
functiontest(propOrders){
items.sort(function(a,b){
returnSortByProps(a,b,propOrders);
});
console.log(items);
}
functiontestAsc(){
test({"name":"asc","value":"asc"});
}
functiontestDesc(){
test({"name":"desc","value":"desc"});
}
functiontestAscDesc(){
test({"name":"asc","value":"desc"});
}
functiontestDescAsc(){
test({"name":"desc","value":"asc"});
}
TypeScript代码
/**
**排序方向。
*/
typeDirect="asc"|"desc";
/**
**排序属性。
**
**@interfaceIPropertyOrder
*/
interfaceIPropertyOrder{
[name:string]:Direct;
}
/**
**简单名/值对象。
**
**@interfaceISimpleObject
*/
interfaceISimpleObject{
[name:string]:string|number|boolean;
}
/**
**对简单的名/值对象按照指定属性和排序方向进行排序(根据排序属性及排序方向,
**对两个项依次进行比较,并返回代表排序位置的值)。
**
**@templateT简单的名/值对象。
**@param{T}item1排序比较项1。
**@param{T}item2排序比较项2。
**@param{...IPropertyOrder[]}props排序属性。
**@returns若项1大于项2返回1,若项1等于项2返回0,否则返回-1。
*/
functionSortByProps<TextendsISimpleObject>
(item1:T,item2:T,...props:IPropertyOrder[]){
"usestrict";
varcps:Array<number>=[];//存储排序属性比较结果。
//如果未指定排序属性,则按照全属性升序排序。
varasc=true;
if(props.length<1){
for(varpinitem1){
if(item1[p]>item2[p]){
cps.push(1);
break;//大于时跳出循环。
}elseif(item1[p]===item2[p]){
cps.push(0);
}else{
cps.push(-1);
break;//小于时跳出循环。
}
}
}else{//按照指定属性及升降方向进行排序。
for(vari=0;i<props.length;i++){
varprop=props[i];
for(varoinprop){
asc=prop[o]==="asc";
if(item1[o]>item2[o]){
cps.push(asc?1:-1);
break;//大于时跳出循环。
}elseif(item1[o]===item2[o]){
cps.push(0);
}else{
cps.push(asc?-1:1);
break;//小于时跳出循环。
}
}
}
}
for(varj=0;j<cps.length;j++){
if(cps[j]===1||cps[j]===-1){
returncps[j];
}
}
return0;
}
使用场景及局限性
在前端使用JavaScript实现多属性排序,减少了对服务器端的请求,减轻服务器端的计算压力,但是也仅适用于只需要对本地数据进行排序的情形。如果需要对整个数据集进行多属性排序,最终还是要在服务器端的数据库层面上进行。
以上所述是小编给大家介绍的JavaScript对象数组如何按指定属性和排序方向进行排序的全部叙述,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对毛票票网站的支持!