数组函数“十大酷刑”?这排序规则我来帮你盘明白!
搞编程的兄弟姐妹们,谁没被数组函数的排序折腾过?明明想排个数,结果它给你来个“字典序”,直接把10排在2前面。那种感觉,就像你按身高排队伍,它却按名字首字母排,场面一度十分尴尬。
我前两天帮一个刚入门的朋友调bug,就栽在这上面。他说:“我按价格给商品排序,最贵的怎么跑最前面去了?”我一看,好家伙,字符串比大小,"999"确实比"100"“大”。这种坑,没踩过的人真不懂。
今天咱就不整那些花里胡哨的教科书定义了。我来当回“翻译官”,把那些数组函数背后的排序逻辑,用大白话给你盘清楚。你会发现,搞懂这几个核心“脾气”,比记十个函数名都有用。
说人话版:“排序”到底在排什么?
数组函数排序,表面看是排大小,底层其实是定“比较规则”。计算机不认识“大小”,它只认识“顺序”。所以你得告诉它,A和B放一起,谁应该在前。

这个“告诉”的过程,就是给它一套比较标准。而我们常用的那些排序函数,默认都内置了一套“懒人规则”。
1. 默认规则:字典序(Lexicographical Order)
这是最容易坑人的规则。字符串“10”和“2”比较时,它会像比单词一样,从左到右逐个字符比。先比第一个字符“1”和“2”,发现“1”<“2”,于是得出结论:“10” < “2”。
说白了,它就是把数字当成一串字母来排。 这不是bug,是特性。很多语言(比如JavaScrIPt的sort())默认就这么干。所以,排数字前,你几乎总得先把它变成真正的数字。
2. 数值排序(Numeric Sort)
这是咱们最想要的。1肯定比2小,10也肯定比2大。要达到这个效果,你必须在排序时提供一个“比较函数”。
比如在JavaScript里,你得写成arr.sort((a, b) => a - b)。这行代码的意思是:“你(函数)去比较a和b,如果a - b是负数,a就排前面;如果是正数,a就排后面;0就不管。” 简单粗暴,但有效。
3. 大小写敏感排序(Case-Sensitive)
排英文单词时,默认的“字典序”是认大小写的。大写字母A-Z排在小写字母a-z的前面。因为它们在Unicode编码表里的位置不同。
这意味着,["Apple", "banana", "Cat"]排序后,会变成["Apple", "Cat", "banana"]。想要“a”和“A”一视同仁?那你得先统一转成小写或大写再排。
实战派:这些“潜规则”怎么用?
知道了默认坑,咱就能绕着走了。下面这几个场景,你大概率会遇到。
场景一:排数字数组,千万别省心
const prices = ["199", "25", "8.99", "1000"];
// 错误示范(默认字典序):["1000", "199", "25", "8.99"] 完全乱套
// 正确做法:转成数字再排
prices.sort((a, b) => parseFloat(a) - parseFloat(b));
// 结果:["8.99", "25", "199", "1000"] 完美
(你看,转个型的事,但忘了这步就完蛋。)
场景二:排对象数组,得告诉它按哪个字段排
这是进阶用法,也是最常用的。比如你有个商品列表,想按销量从高到低排。
const goods = [
{ name: "键盘", sales: 1200 },
{ name: "鼠标", sales: 3500 },
{ name: "耳机", sales: 800 }
];
// 按销量降序排
goods.sort((a, b) => b.sales - a.sales);
// 结果:鼠标(3500) > 键盘(1200) > 耳机(800)
核心思想没变:给一个比较函数,返回值的正负决定顺序。
场景三:中文排序,需要点特殊关怀
中文默认按Unicode码点排,结果可能让你哭笑不得(比如按拼音首字母乱排)。想要按拼音排序?得引入第三方库,或者利用浏览器内置的localeCompare方法。
const cities = ["上海", "北京", "广州", "深圳"];
cities.sort((a, b) => a.localeCompare(b, 'zh-CN'));
// 大概率会按拼音排序了
给追求极致的你:一些冷知识和骚操作
1. 稳定排序很重要
什么是稳定排序?如果两个元素比较起来是“相等”的,那么它们在排序后的相对位置保持不变。ES2019规范开始,Array.prototype.sort被要求必须是稳定排序。这意味着,先按某个次要字段排过的顺序,在按主要字段排序时,相同的主字段值内部,次要顺序不会乱。这个特性在复合排序时特别有用。
2. 链式调用:先加工,再排序
有时候数据源不太“干净”,你可以先.map()处理一下,再.sort()。
const rawData = ["100件", "5件", "20件"];
// 先提取数字,再排序
const sorted = rawData
.map(item => parseInt(item))
.sort((a, b) => a - b); // [5, 20, 100]
3. 自己动手,丰衣足食:实现一个排序算法
总用内置的,有时候不满足特定需求(比如对内存极度敏感)。经典的快排、归并、堆排,理解其思想后,你可以针对特定数据模式(比如几乎有序的数组)写出更高效的自定义排序。
别怕,这事儿没那么玄乎
说到底,数组函数排序就是一套游戏规则。默认规则可能不是你想要的,但好就好在,几乎所有语言都允许你通过自定义比较函数来“改规则”。
记住几个关键点:
- 排数字,先转换,永远警惕默认的字典序。
- 排对象,抓字段,比较函数里指明你要比较的“key”。
- 理解稳定性,它在某些场景下是救命的。
- 别忽视边界,比如空数组、null值、不同类型的数据混排。
编程里很多东西都是这样,坑踩多了,就成了经验。下次再排序,别再下意识地直接用.sort()了,花两秒钟想一想:我到底想按什么规则排? 这两秒钟,能省你俩小时debug的时间。
行了,关于数组排序的这些“潜规则”就唠到这儿。去试试吧,下次排序,你肯定能排得明明白白。

还没有评论,来说两句吧...