JavaScript高级函数之排序sort

简介: 引入排序算法作为各学校算法入门的必修课程,一定给各位留下过深刻的印象。不论是经典的冒泡排序、选择排序,还是当今最高效的快速排序,亦或者是比较有趣的栈排序、桶排序,都凝聚了前人过人的智慧和创造力。不过随着高级语言的出现,似乎当今使用来看我们并不需要每次都自己手码排序算法。像python语言,内置的sorted函数和sort方法允许使用者直接调用来给选定序列排序,并且内部是用c语言基于快速排序编写的,运行效率和占用情况都大大高于我们手码排序算法。JS也是这样一门高级语言,其带有sort函数可以直接给指定序列排序今天就让我们一起了解一下js的sort有什么有趣的地方吧——

引入


排序算法作为各学校算法入门的必修课程,一定给各位留下过深刻的印象。不论是经典的冒泡排序、选择排序,还是当今最高效的快速排序,亦或者是比较有趣的栈排序、桶排序,都凝聚了前人过人的智慧和创造力。

不过随着高级语言的出现,似乎当今使用来看我们并不需要每次都自己手码排序算法。像python语言,内置的sorted函数和sort方法允许使用者直接调用来给选定序列排序,并且内部是用c语言基于快速排序编写的,运行效率和占用情况都大大高于我们手码排序算法。

JS也是这样一门高级语言,其带有sort函数可以直接给指定序列排序

今天就让我们一起了解一下js的sort有什么有趣的地方吧——


1. sort方法调用


前面我们介绍过js中“方法”的概念。本质上是和对象建立关联的函数。sort函数默认和array对象建立了关联。所有的array可以直接调用sort方法来进行排序。我们看看效果——


['juejin', 'aliyun', 'cnds'].sort(); // 输出结果为 ['aliyun', 'cnds', 'juejin']


对字符串的排序是从第一个开始一个个字符比对ASCII码来实现的。a<c<j,所以我们自然就得到了如上的输出结果。如果出现首个字符相同,则比较第二个……以此类推

但要注意,大小写字符的ASCII码是不一样的(如果一样的话电脑怎么区分大小写呢?)所以可能会出现下面的情况——


['Juejin', 'aliyun', 'cnds'].sort(); // 输出结果为 [ 'Juejin','aliyun', 'cnds']


如上结果。J是大写字母,大写字母的ASCII码要在小写之前,因而排在最后的j换成大写J了以后直接排在了最前面。

那么针对数值的排序是怎么样的呢?我们来实践一下


[101, 208, 105, 2000].sort(); // [101, 105, 2000, 208]


结果不是我们想要的数值排序。而仍然是字符串排序结果。说明在js中,sort会自动把所有元素转化成字符串类型,再按照字符串的比较方式进行排序


2. sort的自定义排序


那么我们要实现数值的排序应该怎么办呢?这时候就要引入我们的sort函数自定义功能了。

我们可以在sort()中传入一个自定义函数,构造规则一般如下:

设置两个参数,一般为x,y

用分支语句进行条件判断

如果不符合预期排序前后顺序的则return 1,符合预期顺序的return -1,相等时return 0。

举个具体的例子吧。如果我们希望按照数值排序,则可以采用如下代码构造函数


var arr = [101, 208, 105, 2000];
arr.sort(function (x, y) {
    if (x < y) {
        return -1;
    }
    if (x > y) {
        return 1;
    }
    return 0;
}); // [101, 105, 208, 2000]


设置function之后sort便不会直接把array的所有元素转为字符串,而是直接针对函数里的规则进行判断。

在这里我们发现,如果x<y,这说明了小的在前面,符合了我们的预期,于是return -1。x>y是大的数在前面,不符合预期,我们返回1。

其余情况为相等,我们返回0(其实不设置这个也可以,因为相等的数不需要改变顺序。但为了考虑xy相等的情况,如果我们舍弃了return 0,最好把等号放到条件判断里,如把x<y改成x<=y或者把x>y改成x>=y)

在这里不设置return 0并没有任何影响:


image.png


我们发现输出值是正确的。

那么请问,倒序的时候应该如何设置呢?

其实很简单,只要把“预期顺序”改变就行,之前我们希望x,y这种顺序是x<y时出现的,所以在x<y的时候return了-1,而现在我们改变了预期,便要让x<y的时候return 1。x>y的情况亦然。修改代码后如下:


arrr = [10, 20, 10, 1, 2, 20, 20, 26, 80]
arrr.sort(function (x, y) {
    if (x <= y) {
        return 1;
    }
    if (x > y) {
        return -1;
    }
});
console.log(arrr);


image.png

image.png


输出正常。


3. 关于sort的一点思考


很多人一看到这个x,y相比较,就感觉像是冒泡排序

其实这样理解是不对的,冒泡是效率非常低的,业内不会设计由冒泡来作为排序函数的底层逻辑

事实上,js的sort底层原理是由引擎实现的,可能有涉及timsort、快排等多种排序算法。我们作为js使用者、前端工程师而非算法设计师不必细究。有兴趣可以作为拓展知识了解。

此外,关于-1和1,其实在实际使用中可以直接用任意负数、正数替代。这样一来,对数值的排序可以简化成:


arrr = [10, 20, 10, 1, 2, 20, 20, 26, 80]
arrr.sort(function (x, y) {
    return x-y
});
console.log(arrr); 


当x<y的时候,x-y<0,负数,表示符合预期(x小y大,x,y这种x在前y在后的顺序是正确的)。

反之x>y, x-y>0, 正数,不符合预期,因为此时x,y这种大在前小在后的顺序是不应该出现的。

x==y的时候相减为0,0本身也不会影响排序先后,可以忽略。

这就构造了一个升序sort:


image.png

输出正常


还有关于我上面提到的“符合预期”理解法,其实是经过我个人简化后的理解。按照主流理解逻辑,function中x和y的关系应该是这样理解的,个人感觉逻辑上需要绕一绕

sort本身只会升序排序,即从小到大。但什么算小,什么算大是可以由我们决定的。

我们只需要举两个数作为例子,告诉计算机什么情况算小,什么情况算大即可

这个“告诉”的规则是,如果对传入的x,y而言,x<y则返回-1,x>y返回1,x==y返回0.

对于数字而言,我们传入的xy分别代表一个数,如果是升序,则符合默认sort逻辑,无需改动。

但如果是降序,我们需要改变计算机认为的大小逻辑,此时x>y时需要让计算机认为是x<y。

举个例子,我们传入的一串数是2,3,1,希望升序排序。sort会的就是升序,于是我们按照我们的逻辑告诉计算机当x比y小时,你要当作x比y小(比如正常逻辑是1小于3,你就让计算机当作1小于3)。计算机sort升序之后就是1,2,3

当我们传入2,3,1,希望降序排序。但是sort只知道升序啊,那我们要实现降序,就得告诉计算机:当x<y的时候,你要当作x>y呀(比如正常逻辑是1小于3,但你告诉计算机1大于3),于是计算机按照他的“新逻辑”升序排序之后,排出来的结果对我们正常逻辑就是降序的。排出来就是3,2,1

这个“新逻辑”不光可以运用在数上,你可以建立任何的“新逻辑”,比较经典的是我们可以忽略大小写判断字符串大小:


var arr = ['Juejin', 'aliyun', 'cnds'];
arr.sort(function (x, y) {
    x1 = x.toUpperCase();
    y1 = y.toUpperCase();
    if (x1 < y1) {
        return -1;
    }
    if (x1 > y1) {
        return 1;
    }
    return 0;
}); // 输出结果为['aliyun', 'cnds', 'Juejin']


我们相当于告诉了计算机,当x1<y1的时候(也就是所有元素都转化成大写的x和y),我们告诉电脑要此时x和y的关系要当作x<y。反之同理。这就完成了一次对字符不区分大小写的升序。

略有冗长,如果一下子有点难理解,不妨还是用简便理解法。但既然想体验coding之美,不如还是尝试着自己想想吧


相关文章
|
14天前
|
JavaScript 前端开发 安全
JavaScript函数详解
JavaScript函数的详细解析,包括函数的定义和调用方式(如一般格式、匿名函数、构造函数、自调用函数、箭头函数和严格模式)、函数参数(arguments对象、可变参数、默认参数值)、闭包的概念和应用实例。
JavaScript函数详解
|
1天前
|
JavaScript 前端开发
在JS中如何声明一个函数
在JS中如何声明一个函数
7 2
|
13天前
|
JavaScript 前端开发
JavaScript函数可以返回两个值
JavaScript函数可以返回两个值
|
1月前
|
JavaScript 前端开发
JavaScript基础知识-函数的返回值
关于JavaScript函数返回值的基础知识。
24 9
JavaScript基础知识-函数的返回值
|
13天前
|
自然语言处理 分布式计算 JavaScript
JavaScript函数
JavaScript函数
|
23天前
|
JSON JavaScript 前端开发
JavaScript第五天(函数,this,严格模式,高阶函数,闭包,递归,正则,ES6)高级
JavaScript第五天(函数,this,严格模式,高阶函数,闭包,递归,正则,ES6)高级
|
16天前
|
JSON JavaScript 数据格式
手写JS实现深拷贝函数
本文介绍了如何实现一个深拷贝函数`deepClone`,该函数可以处理对象和数组的深拷贝,确保拷贝后的对象与原始对象在内存中互不干扰。通过递归处理对象的键值对和数组的元素,实现了深度复制,同时保留了函数类型的值和基础类型的值。
17 3
|
1月前
|
JavaScript 前端开发
JavaScript基础知识-函数的参数
关于JavaScript函数参数基础知识的介绍。
18 4
JavaScript基础知识-函数的参数
|
14天前
|
缓存 JavaScript 前端开发
了解js基础知识中的作用域和闭包以及闭包的一些应用场景,浅析函数柯里化
该文章详细讲解了JavaScript中的作用域、闭包概念及其应用场景,并简要分析了函数柯里化的使用。
了解js基础知识中的作用域和闭包以及闭包的一些应用场景,浅析函数柯里化
|
16天前
|
前端开发 数据可视化 开发者
D3.js 内置的动画函数
D3.js 内置的动画函数