Open sunmaobin opened 6 years ago
最近在使用数组排序的时候,一不小心犯了一个低级错误,分享一下,大家引以为戒。同时,提供一种能应对数组中含有各种特殊符号的排序方式。
在项目中需要将一个数组排序,于是我大致就是这么写的:
//示例,由大到小排序 var ary = [1,3,5,4]; ary.sort(function(a,b){ return a < b; }); //结果:[5,4,3,1]
由于忘记自定义sort函数了,简单在控制台Console里跑了一下,这个是OK的,于是代码就这么去写了,没想到就出问题了。。。
问题是什么呢?看下面的例子:
var ary = [5, 8, 7, 1, 2, 3, 4, 6, 9, 10, 11, 12, 13]; ary.sort(function(a,b){ return a < b; }); //结果:[4, 13, 6, 7, 12, 11, 8, 10, 9, 5, 3, 2, 1]
结果就开始出问题了!
于是仔细看 Array.sort 的API才发现自定义sort函数的返回值并不是 true or false:
Array.sort
true
false
> 0
== 0
< 0
也就是说返回:正数、负数和0!
var ary = [5, 8, 7, 1, 2, 3, 4, 6, 9, 10, 11, 12, 13]; ary.sort(function(a,b){ if (a < b) return 1; if (a > b) return -1; /* else */ return 0; }); //结果:[4, 13, 6, 7, 12, 11, 8, 10, 9, 5, 3, 2, 1]
上面还有一种简写方法,就是:
ary.sort(function(a,b){ return b-a; });
但是这种做法一定要保证数组中全部是 number 类型的,才可以这么简写,要不然最好在function中判断下再处理。
number
比如:
["5", "8", "7", "string", undefined, "3", {}, "6", null, "10", "11", "12", "13"].sort(function(a, b) { return a - b; }); //结果:[null, "3", "5", "6", "7", "8", "10", "11", "12", "string", "13", {…}, undefined]
其实,一般的排序都是针对 数字 的,也就是可以相加减,那么比较起来就比较直观,但是有时候我们排序的内容有可能不只是纯数字,里面可能有字母,甚至中文,这时候怎么办?
数字
JS有个方法:localeCompare,用法参考:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare
localeCompare
这个方法就是提供本地化的比较方案,详细的使用说明,大家可以看看上面的文档,这里给出一种数组中含有中文、英文、数字、汉字等字符时的通用比较方案。
先给出一组比较复杂的数组:
let testAry = [100,'汉字','中文','澳门',200,'Admin',30,'Lisa',undefined,null,'','%','&'];
先使用传统的方式:
testAry.sort(function(a,b){ return a - b; }); //["", null, 30, "澳门", 100, "Admin", "汉字", "Lisa", "&", "中文", 200, "%", undefined]
当各种情况混合到一起的时候,传统的相减的比较,完全不能达到要求,下来我们看看使用 localeCompare的方案:
testAry.sort(function(a,b){ var r = a - b; if(isNaN(r)){ r = String(a).localeCompare(String(b), 'zh-CN', {sensitivity: 'accent'}); }; return r; }); //["", "&", "%", null, 30, 100, 200, "澳门", "汉字", "中文", "Admin", "Lisa", undefined]
结果明显要好很多,至少除了特殊字符,中英文和数字都是合理的排序,我们稍微改进下,让其按照如下的规则排序,就完美了:
其实,就是将排好序的数组,重新处理下就好了,增加一个方法:
function arySort(ary){ ary.forEach(function(d){ if(!d && d !== 0){ ary.a = ary.a || []; ary.a.push(d); return true; }; if(/\d+/.test(d)){ ary.b = ary.b || []; ary.b.push(d); return true; }; if(/[a-zA-Z]+/.test(d)){ ary.c = ary.c || []; ary.c.push(d); return true; }; //注意:汉字的正则不推荐:/[\u4e00-\u9fa5]/ //参考:https://zhuanlan.zhihu.com/p/33335629 if(/\p{Unified_Ideograph}/u.test(d)){ ary.d = ary.d || []; ary.d.push(d); return true; }; ary.e = ary.e || []; ary.e.push(d); }); return [].concat(ary.b || []) .concat(ary.c || []) .concat(ary.d || []) .concat(ary.e || []) .concat(ary.a || []) };
完整的解决方案代码:
let testAry = [100,'汉字','中文','澳门',200,'Admin',30,'Lisa',undefined,null,'','%','&']; function arySort(ary){ //重新定义ary,不污染外部数组 ary = [].concat(ary); //使用localeCompare排序 ary.sort(function(a,b){ var r = a - b; if(isNaN(r)){ r = String(a).localeCompare(String(b), 'zh-CN', {sensitivity: 'accent'}); }; return r; }); //将排序后的数组重新分类 ary.forEach(function(d){ if(!d && d !== 0){ ary.a = ary.a || []; ary.a.push(d); return true; }; if(/\d+/.test(d)){ ary.b = ary.b || []; ary.b.push(d); return true; }; if(/[a-zA-Z]+/.test(d)){ ary.c = ary.c || []; ary.c.push(d); return true; }; if(/\p{Unified_Ideograph}/u.test(d)){ ary.d = ary.d || []; ary.d.push(d); return true; }; ary.e = ary.e || []; ary.e.push(d); }); return [].concat(ary.b || []) .concat(ary.c || []) .concat(ary.d || []) .concat(ary.e || []) .concat(ary.a || []) }; let result = arySort(testAry); //[30, 100, 200, "Admin", "Lisa", "澳门", "汉字", "中文", "&", "%", "", null, undefined]
下面再验证几个典型的场景:
纯数字
arySort([100,22,0,3,10001]) //[0, 3, 22, 100, 10001]
注意:这里容易出现的错误就是 22 > 100 原因是把这2个按照字符串比较,这样100就排在22前面了。
纯英文
arySort(['hello','world','array','alert','boy','girl']) //["alert", "array", "boy", "girl", "hello", "world"]
注意:alert 和 array,首字母相同时,比较第2个字符,依次类推,这就是字典顺序。
纯中文
arySort(['你好','我们','中国人','比较','腼腆','害羞','奥运']) //["奥运", "比较", "害羞", "腼腆", "你好", "我们", "中国人"]
数字和英文
arySort([100,22,'array','sort',35,'lisa','vivo']) //[22, 35, 100, "array", "lisa", "sort", "vivo"]
中英文和数字
[22, 35, 100, "array", "lisa", "sort", "vivo", "日本", "中国"]
最近在使用数组排序的时候,一不小心犯了一个低级错误,分享一下,大家引以为戒。同时,提供一种能应对数组中含有各种特殊符号的排序方式。
问题
在项目中需要将一个数组排序,于是我大致就是这么写的:
由于忘记自定义sort函数了,简单在控制台Console里跑了一下,这个是OK的,于是代码就这么去写了,没想到就出问题了。。。
问题是什么呢?看下面的例子:
结果就开始出问题了!
原因
于是仔细看
Array.sort
的API才发现自定义sort函数的返回值并不是true
orfalse
:> 0
when a is considered larger than b and should be sorted after it== 0
when a is considered equal to b and it doesn't matter which comes first< 0
when a is considered smaller than b and should be sorted before it也就是说返回:正数、负数和0!
解决
上面还有一种简写方法,就是:
但是这种做法一定要保证数组中全部是
number
类型的,才可以这么简写,要不然最好在function中判断下再处理。比如:
深入
其实,一般的排序都是针对
数字
的,也就是可以相加减,那么比较起来就比较直观,但是有时候我们排序的内容有可能不只是纯数字,里面可能有字母,甚至中文,这时候怎么办?JS有个方法:
localeCompare
,用法参考:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare这个方法就是提供本地化的比较方案,详细的使用说明,大家可以看看上面的文档,这里给出一种数组中含有中文、英文、数字、汉字等字符时的通用比较方案。
先给出一组比较复杂的数组:
先使用传统的方式:
当各种情况混合到一起的时候,传统的相减的比较,完全不能达到要求,下来我们看看使用
localeCompare
的方案:结果明显要好很多,至少除了特殊字符,中英文和数字都是合理的排序,我们稍微改进下,让其按照如下的规则排序,就完美了:
其实,就是将排好序的数组,重新处理下就好了,增加一个方法:
完整的解决方案代码:
下面再验证几个典型的场景:
纯数字
纯英文
纯中文
数字和英文
中英文和数字
感悟
参考