JavaScript的排序机制是一种简单的模型,但是冒泡是一些难以置信的灵活和强大的功能。 使用sort
不仅可以按字母顺序或数字顺序组织数组,还可以按条件逻辑表示的任何定制排列形式组织数组。
排序功能如何运作
→如果您已经了解基础知识,则可能要跳过 。
如果没有参数调用sort()
,则将每个值都视为字符串时,按字典顺序对数组进行字典排序:
var letters = ["R","O","F","L"];
letters.sort();
alert(letters); //produces ["F","L","O","R"]
否则, sort
的参数是一个比较函数 ,它根据返回的方式定义排序行为。 比较函数本身接受两个参数,通常称为a
和b
,它们表示每个操作中要比较的两个值。 然后:
- 如果函数返回小于零 ,那种
a
前b
- 如果函数返回大于零 ,排序
b
前a
- 如果函数返回零 ,则使
a
和b
保持不变
JavaScript规范将第一个排序条件称为
。 但这实际上意味着“将b
,其索引比a
索引低b
在列表的下方,而不是a
” ,就数字索引而言,它是较高的索引,而不是较低的索引。 它以一种非常混乱的方式使用“索引”一词。 我希望如何表达上述条件应该更加清楚。
因此,使用比较功能的通常方法是执行并返回产生所需排序的简单评估。 例如,如果函数返回(a - b)
,则将产生数字排序 :
var numbers = [8,5];
numbers.sort(function(a, b)
{
return a - b;
});
alert(numbers); //produces [5,8]
我们可以通过值示例来解决这一问题:由于a = 8
且b = 5
,因此(a - b) == 3
; 3大于零,因此b
将在a
之前排序,从而产生顺序[5,8]
。
因此,可以通过简单地逆转方程式来产生反数字顺序:
var numbers = [4,3,5,9];
numbers.sort(function(a, b)
{
return b - a;
});
alert(numbers); //produces [9,5,4,3]
我们还可以通过定义三个比较来评估每对字符串,从而创建一个产生字典排序的比较函数-在计算方面, "a"
小于"b"
,因此我们可以像这样直接比较字符串,然后返回三个排序值之一:
var letters = ["R","O","F","L"];
letters.sort(function(a, b)
{
var x = a.toLowerCase(), y = b.toLowerCase();
return x < y ? -1 : x > y ? 1 : 0;
});
请注意如何将每个字符串预先转换为小写,以确保获得不区分大小写的排序(如果不这样做,则大小写字母将分别进行排序)。 我们还将这些操作的结果分配给新变量,因为某些浏览器反对将参数覆盖。
多维排序
如果a
和b
本身是数组,那么,使用数学评估直接比较数组不会产生我们想要的结果; 但是我们可以比较它们的内在价值并对其进行排序。 这就是我们使用每个内部数组中的值作为排序标准对多维数组进行排序的方式。 所有其他内部值都照常运行,因此我们可以对包含值混合的数组进行排序。 以下示例将按每种形状的边数对矩阵进行排序:
var shapes = [
[5, "Pentagon"],
[3, "Triangle"],
[8, "Octagon"],
[4, "Rectangle"]
];
shapes.sort(function(a, b)
{
return a[0] - b[0];
});
多标准排序
如果我们仅可以使用一个值对多维数组进行排序,那么我们是否也可以使用两个值作为独立条件对它们进行排序? 答案是肯定的,我们可以,只需在比较函数内部的逻辑中添加更多条件即可。 例如,将值[0]
用于主要排序,但如果两个值相等,则将值[1]
用于次要排序。 下面的示例再次使用形状,如果边数相等,则首先按边数排序,然后按形状的字母名称排序:
var shapes = [
[4, "Trapezium"],
[5, "Pentagon"],
[3, "Triangle"],
[4, "Rectangle"],
[4, "Square"]
];
shapes.sort(function(a, b)
{
if(a[0] === b[0])
{
var x = a[1].toLowerCase(), y = b[1].toLowerCase();
return x < y ? -1 : x > y ? 1 : 0;
}
return a[0] - b[0];
});
主体可以扩展到我们需要的程度—如果主要测试相等,则按次要测试排序; 如果二次测试相等,则按三次测试进行排序; 以此类推,就我们所拥有的尽可能多的比较点而言。
排序对象数组
随着比较变得越来越复杂,最好放弃使用多维数组,而转而使用object-literal数组 。 仅仅因为我们有标准的直观名称 ,这使得更容易查看比较功能中发生的事情。 一个很好的例子可以在CSSUtilities库中看到,该库解析文档CSS以创建自己的规则对象集合。
总体规则集合存储为一个数组,每个成员都是一个对象,其属性包括specificity
(规则的“强度”,由其选择器和继承上下文确定), index
(规则在规则中的整体位置)。规则集合)和depth
(表示继承链深度的继承规则的数值,即,从<html>
继承的规则的值比从<body>
继承的规则的值大(一个))。 。 specificity
本身也是四个独立值的数组,每个特异性值一个(有关详细信息,请参阅CSS3规范中的计算选择器的特异性 )。
那么,我们如何考虑所有这些值对规则对象进行排序,以获得一个以绝对绝对顺序排列的数组? 当然,第一件事是要清楚地了解我们要实施的规则:
- 如果值不相等,则按特异性排序:
- 如果值不相等,则按第一类排序
- 否则,如果值不相等,则按第二个类别排序
- 否则,如果值不相等,则按第三类排序
- 否则按第四和最后一个类别排序
- 否则按索引排序(如果值不相等)
- 否则就继承深度排序
然后,这只是用代码表示的情况:
rules.sort(function(a, b)
{
if(a.specificity.toString() === b.specificity.toString())
{
if(a.index === b.index)
{
return b.depth - a.depth;
}
return a.index - b.index;
}
if(a.specificity[0] !== b.specificity[0])
{
return a.specificity[0] - b.specificity[0];
}
if(a.specificity[1] !== b.specificity[1])
{
return a.specificity[1] - b.specificity[1];
}
if(a.specificity[2] !== b.specificity[2])
{
return a.specificity[2] - b.specificity[2];
}
return a.specificity[3] - b.specificity[3];
});
逻辑有些混乱,因此某些规则表示为逆条件。 这是为了提高函数的效率,因此只需较少的代码即可实现,并尽快返回 。 编码相同条件的方法可能有几种不同的方式。
关于稳定排序的注释
这种技术唯一真正的问题是稳定排序问题,这意味着-如果a
和b
相同,则它们彼此之间不会改变。 问题是稳定排序本身就是可排序的值 ; 但是在这些示例中, a
和b
本身并不是我们正在评估的值,它们仅仅是这些值的容器 。 因此,不能保证稳定的排序,并且实际发生的情况在不同的浏览器中会有所不同(有些会离开它们,有些会移动它们)
就我个人而言,我从来没有发现这种情况很重要。 但是,如果这样做,防止它的方法是确保没有两个可排序对象完全相同 。 例如,您可以为要排序的每个对象分配一个数字索引属性,以反映它们在数组中的初始顺序。 然后在比较函数中,为所有其他条件相等时添加一个最终条件,该条件按这些索引的值排序。 由于它们反映了原始顺序并且都是唯一的,因此只要没有其他排序,它们就会有效地维护顺序。
排序!
要记住的基本事情是排序比较函数没有什么特殊或不同寻常 ,它只是另一个函数,先进行填充然后返回。 您可以加载外部数据,创建用于测试渲染的元素或执行任意数量的复杂操作。 只要函数正确返回(小于零,大于零或零),那么到达目标位置就没有特别的限制!
缩图: [Soren]
From: https://www.sitepoint.com/sophisticated-sorting-in-javascript/