对象数组排序-使用sort和闭包

最近感觉有些迷茫,所以又重新开始读书了,读的依然是那本“javascript高级程序设计”,每次读的时候,都会有一些些不同的收获,这里就分享一个简单的小的技巧?算是技巧吗,只能说是一个小小的技术点吧。

一:概述

这是在第五章的数组中,看到的,其实也是一直都知道,但是却不长使用的一个点吧,对于我来说,JS中的很多东西,我都有做过一些研究,只是当换个环境时,却总是不能把一些知识点和实际应用环境结合起来,这里也是~~我一直都是的数组对象中的sort方法,可以传入一个函数,然后可以根据函数的返回值进行一个自定义的排序,但是当这个数组的元素,变成一个对象时,就不长能想到,可以使用这个方法,进行排序的问题。这估计就是传说中的反应迟钝吧。

二:先说说sort吧

JS的数组对象中,有一个排序的方法,名为sort,这个就没啥说的了吧,默认为升序排列,但是排序的过程中,却是把元素值改为string类型进行比较的,所以就会出现下面的情况:


var arr = [7,23,123,321,23,65];
arr.sort();
console.log(arr.join(","));
//123,23,23,321,65,7


所以,当我们使用sort想要去给数字排序的话,就需要我们自己进行额外处理了,这也就说到了sort的传参的方法了,所以,我们可以如下的去处理了:


var arr = [7,23,123,321,23,65];
arr.sort(function(v1,v2){
    return v1 - v2;
});
console.log(arr.join(","));
//7,23,23,65,123,321


sort传入的函数的,如果返回值是正数的话,则进行调换,其他情况,不进行调换。所以,前面的示例中,sort传入的函数,返回值为v1 - v2也就表示着,如果v1>v2则,二者位子互换,所以如此则可以在排序后,获取到升序排列的数组。如果想要降序排序,那么只需要把return v2-v1;即可。

OK,这里的简单方法,就说到这里了。

三:使用sort给对象数组按照属性排序

之前只是对比数组元素为基本类型时的处理,当数组元素变为对象时呢?一直没有做过这方面的需求,所以也没有想到过这些东西,这次重新读书,很偶然的就想到了这个,使用上述的传入函数作为参数,并且结合闭包即可实现。


var arr = [{name:"Zachary",age:38},{name:"asda",age:28},{name:"Nicholas",age:29}],
    name = "age";

arr.sort(function(obj1,obj2){
    //如果单纯的直接使用,这个时候,obj1和obj2都是数组的对象
    //这个时候,如果我们需要按照对象中的单个属性进行对比排序
    //就要定义一个全局变量:name
    return obj1[name] - obj2[name];
});
console.log(arr);
//[{name:"asda",age:28},{name:"Nicholas",age:29},{name:"Zachary",age:38}]


上述的方法,虽然可以实现我们的要求,但是总归多出了全局变量name,并且若多次使用该方法,那么要一直改变name的值,比较不方便。

所以,为了减少全局变量,很自然的就想到了闭包的方案,所以也就有了下面的方法:


var data = [{name:"Zachary",age:38},{name:"asda",age:28},{name:"Nicholas",age:29}];

function sortArrObjectName(name,fn){
    //name为按照该属性进行查证
    //fn为自定义的排序方案。
    //如果有fn,则按照fn进行处理
    //否则,按照默认的升序排序进行处理
	
    //使用了闭包,返回一个新的函数,作为sort的处理函数
    return (typeof fn == "function")?function(obj1,obj2){
	return fn(obj1[name],obj2[name]);
    }:function(obj1,obj2){
	var v1 = obj1[name],
	    v2 = obj2[name];
			
	if(v1 < v2){
	    return -1;
	}else if(v1 > v2){
	    return 1;
	}else{
	    return 0;
	}
    };
}

function sortType(v1,v2){
    //定义的降序排序的方法
    if(v1 < v2){
	return 1;
    }else if(v1 > v2){
	return -1;
    }else{
	return 0;
    }
}

var aa = data.sort(sortArrObjectName("age"));
console.log(aa);
//[{name:"asda",age:28},{name:"Nicholas",age:29},{name:"Zachary",age:38}]
//上述是按照升序排列的方法,所以得到了上面的数组

//当有第二个参数传入时,比如下面的方法
//var aa = data.sort(sortArrObjectName("age",sortType));
//console.log(aa);
//此时,就算是降序排序了,所以得到的结果就是:
//[{name:"Zachary",age:38},{name:"Nicholas",age:29},{name:"asda",age:28}];


OK,貌似也没有什么好说的,想到了这个方法,来实现的话,就很简单了。

四:扩展一下下

但是前面的方法,依然也只能是处理二维深度的对象进行处理,如果我们需要处理的对象的属性,深度比较大,那又要如何处理呢,虽然想想还是很简单~~~好吧,感觉没有什么好说的。

直接上方法吧:


 function sortArrObjectName(options){
     /*
	 name:表示待查找的属性
	 type:num,string
	 fn:按照自定义的规则进行排序,fn为自定义的排序
     */
	
     return (typeof options.fn == "function")?function(obj1,obj2){
		
	 var v1 = sortArrObjectName.getValue(obj1,options.name),
	     v2 = sortArrObjectName.getValue(obj2,options.name);
	     //静态方法getValue,是为了查找obj的对应的元素值
	     //如果查找不到对应的值,则返回false
		
	 if(v1 === false && v2 === false){
	     //如果出错,则不进行变化
	     return 0;
	 }else if(v1 === false){
	     //false的被排到最后去
	     return 1;
	 }else if(v2 === false){
	     //false的被排到最后去
	     return -1;
	 }
		
	 options.type = options.type || typeof v1;
	 v1 = options.type == "num"?+v1:options.type == "string"?""+v1:v1;
	 v2 = options.type == "num"?+v2:options.type == "string"?""+v2:v2;
		
	 return options.fn(v1,v2);
		
     }:function(obj1,obj2){
	 var v1 = sortArrObjectName.getValue(obj1,options.name),
	     v2 = sortArrObjectName.getValue(obj2,options.name);
		
	 if(v1 === false && v2 === false){
	     //如果出错,则不进行变化
	     return 0;
	 }else if(v1 === false){
	     //false的被排到最后去
	     return 1;
	 }else if(v2 === false){
 	     //false的被排到最后去
	     return -1;
	 }else{
	     options.type = options.type || typeof v1;
	     v1 = options.type == "num"?+v1:options.type == "string"?""+v1:v1;
	     v2 = options.type == "num"?+v2:options.type == "string"?""+v2:v2;
	     if(v1 < v2){
		 return -1;
	     }else if(v1 > v2){
		 return 1;
	     }else{
		 return 0;
	     }
	 }
     };
 }
 sortArrObjectName.getValue = function(obj,str){
     //obj必须为对象
     //str必须为非空的对象
	
     if(typeof obj != "object" || str == "" || typeof str != "string"){
	 return false;
     }

     var arr = str.split("."),
	 i = 0,
	 len = arr.length,
	 result = obj;
	
     for(;i<len;i++){
	 result = result && result[arr[i]];
	 if(result == undefined){
	     break;
	 }
     }
     if(typeof result == "string" || typeof result == "number"){
	 //只有有效数据,才可以进行对比
	 return result;
     }else{
	 //其他数据,则返回一个false,返回到结尾去。
	 return false;
     }
 };
 function sortType(v1,v2){
     if(v1 < v2){
	 return 1;
     }else if(v1 > v2){
	 return -1;
     }else{
	 return 0;
     }
 }

 var data = [
     {name:"Zachary",age:28},
     {name:"Nicholas",age:29},
     {name:"asda",age:{value:28}},
     {name:"Nicholas",age:{value:29}},
     {name:"Nicholas123",age:{value:22}}
 ];
 var aa = data.sort(sortArrObjectName({name:"age.value",fn:sortType}));
 //注意,此时name的值,可以是使用点操作符进行查找目标属性,即按照obj.age.value的值
 //进行排序处理

 console.log(aa);
 /*[
     {name:"Nicholas",age:{value:29}},
     {name:"asda",age:{value:28}},
     {name:"Nicholas123",age:{value:22}},
     {name:"Zachary",age:28},
     {name:"Nicholas",age:29}
  ];*/


因为此时如果找不到对应的属性,会把所属的对象排列到数组的最后,并且不改变这些非法对象的顺序,所以,如果把sortArrObjectNameoptionsfn属性去掉,则会获取到如下的结果:


//var aa = data.sort(sortArrObjectName({name:"age.value"}));
//[{name:"Nicholas123",age:{value:22}},{name:"asda",age:{value:28}},{name:"Nicholas",age:{value:29}},{name:"Zachary",age:28},{name:"Nicholas",age:29}];


当然,这里还有一个属性石type属性,这个是用于对比方式的计算的。
比如:


var data = [
    {name:"Zachary",age:5},
    {name:"Nicholas",age:29},
    {name:"asda",age:{value:28}},
    {name:"Nicholas",age:{value:5}},
    {name:"Nicholas123",age:{value:22}}
];
var aa = data.sort(sortArrObjectName({name:"age.value"}));
console.log(aa);
//[{name:"Nicholas",age:{value:5}},{name:"Nicholas123",age:{value:22}},{name:"asda",age:{value:28}},{name:"Zachary",age:5},{name:"Nicholas",age:29}]


而如果age属性中,获取的值,是字符串类型呢?结果就是如下显示:


var data = [
    {name:"Zachary",age:"5"},
    {name:"Nicholas",age:"29"},
    {name:"asda",age:{value:"28"}},
    {name:"Nicholas",age:{value:"5"}},
    {name:"Nicholas123",age:{value:"22"}}
];

var aa = data.sort(sortArrObjectName({name:"age.value"}));
console.log(aa);
//[{name:"Nicholas123",age:{value:22}},{name:"asda",age:{value:28}},{name:"Nicholas",age:{value:5}},{name:"Zachary",age:5},{name:"Nicholas",age:29}]


所以,当我们无法确定目标数组中,对应属性值,当前保存为number还是string类型时,即可传入type属性,如下:


var data = [
    {name:"Zachary",age:"5"},
    {name:"Nicholas",age:"29"},
    {name:"asda",age:{value:"28"}},
    {name:"Nicholas",age:{value:"5"}},
    {name:"Nicholas123",age:{value:"22"}}
];

var aa = data.sort(sortArrObjectName({name:"age.value",type:"num"}));
console.log(aa);


或者


var data = [
    {name:"Zachary",age:5},
    {name:"Nicholas",age:29},
    {name:"asda",age:{value:28}},
    {name:"Nicholas",age:{value:5}},
    {name:"Nicholas123",age:{value:22}}
];

var aa = data.sort(sortArrObjectName({name:"age.value",type:"num"}));
console.log(aa);


上述的两种情况,都会得到以下的结果:


//[{name:"Nicholas",age:{value:5}},{name:"Nicholas123",age:{value:22}},{name:"asda",age:{value:28}},{name:"Zachary",age:5},{name:"Nicholas",age:29}]


而当如果需要以字符串处理时,type值改为string即可:


data.sort(sortArrObjectName({name:"age.value",type:"string"}));


五:总结

有些东西,我们学会了,当碰到时,也知道怎么用,知道要注意些什么,但是,换个环境,也许就想不到使用了吧,我有时就会出现这样的情况,这里也是给自己一个提醒吧,要不断学习,尤其是基础。

本文地址:http://www.zhangyunling.com/?p=258

发表评论

电子邮件地址不会被公开。 必填项已用*标注

您可以使用这些HTML标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>