[译][undefined, null, NaN].sort();
数组排序是我们不会花长时间考虑的事情之一,直到它停止为我们工作。最近我就使用 javascript 的数组,对一组数据进行排序,但排序结果完全错乱。我花了好长时间才确定哪里出现了问题。
所以,想和大家分享发生了什么以及为什么它的结果如此奇怪。
基本排序
数组有个 sort 排序方法,使用它,会得出我们所预料的排序结果。举个例子:
1const stringArray = ['cat', 'dog', 'ant', 'butterfly'];2stringArray.sort();3// => [ 'ant', 'butterfly', 'cat', 'dog' ]
如果在排序对象中包含 undefined ,也会正确返回结果。MDN 也介绍说,所有的 undefined 对象会放到数组中的结尾。
1const stringArrayWithUndefined = [2 'cat',3 undefined,4 'dog',5 undefined,6 'ant',7 'butterfly',8 'zebra'9];10stringArrayWithUndefined.sort();11// => [ 'ant', 'butterfly', 'cat', 'dog', 'zebra', undefined, undefined ]
陷进
第一个问题,你可能会在一个数组中包含一个 null 。
1const stringArrayWithUndefinedAndNull = [2 'cat',3 undefined,4 'dog',5 undefined,6 'ant',7 null,8 'butterfly',9 'zebra'10];11stringArrayWithUndefinedAndNull.sort();12// => [ 'ant', 'butterfly', 'cat', 'dog', null, 'zebra', undefined, undefined ]
排序将会把 null 强制转换为字符串的 “null”,所以它出现在以字母为排序的一个位置,比如在 ’zebra‘ 前面,因为 n 在 z 前面。
接下来,这里有个只包含数字的数组。默认的排序算法是把数字全部转换为字符串,然后根据他们在 utf-16 中代码顺序进行排序。
刚才已经看到它会正确排序字符串。但它会把数字排成错误的结果。
1const numberArray = [5, 3, 7, 1];2numberArray.sort();3// => [ 1, 3, 5, 7 ]4const biggerNumberArray = [5, 3, 10, 7, 1];5biggerNumberArray.sort();6// => [ 1, 10, 3, 5, 7 ]
可以看到,10 数字排到了 3 的前面,因为字符串 “10” 在字符串 “3”的前面。
我们可以给 sort 函数,传递一个比较函数修复这个问题。比较函数接受两个值,并返回一个大于 0 ,等于 0 或者小于 0 的数字。如果小于0 ,第一个数字在第二个数字的前面。如果大于0 ,第二个参数数字在第一个参数数字的前面。如果等于0,这两个数字则呆在他们原来的位置上。
升序排列一组数字,则传递下面的比较函数
1const compareNumbers = (a, b) => a - b;
上面提到的 biggerNumberArray 数组,使用这个比较函数,会返回正确的排序结果。
1biggerNumberArray.sort(compareNumbers);2// => [ 1, 3, 5, 7, 10 ]
如果所要排序的数组中,含有 undefined 元素,比较函数也会忽略它,并把它放到排序结果的后面。
1const numberArrayWithUndefined = [5, undefined, 3, 10, 7, 1];2numberArrayWithUndefined.sort(compareNumbers);3// => [ 1, 3, 5, 7, 10, undefined ]
传递 null ,又会产生问题。
1const numberArrayWithUndefinedAndNull = [5, undefined, 3, null, 10, 7, 1];2numberArrayWithUndefinedAndNull.sort(compareNumbers);3// => [ null, 1, 3, 5, 7, 10, undefined ]
产生这种情况,是因为强制把 null 转换成数字 0 了 。
1Number(null);2// => 0
可以在 compareNumbers 中处理这种情况或者明确知道这个问题。
真实的案例
最近遇到的最大问题,是 undefined 以另一种方式出现在真实的代码中。正如我们刚刚所看到,如果一个数组中含有 undefined ,它会忽略(不会调用比较函数)并放到数组的结尾。
但如果所要排序数组中是一系列对象,当某一个对象为空对象 ,那最终排序的结果会不稳定。
举个列子,数组中某个对象没有 value ,试着去排序,结果会不符合你的预期。
1const objectArray = [2 { value: 1 },3 { value: 10 },4 {},5 { value: 5 },6 { value: 7 },7 { value: 3 }8];9const compareObjects = (a, b) => a.value - b.value;10objectArray.sort(compareObjects);11// => [ { value: 1 },12// { value: 10 },13// {},14// { value: 3 },15// { value: 5 },16// { value: 7 } ]17
数字和 undefined 相减或者 undefined 和 数字相减,会返回 NaN ,在比较函数中返回的并不是比较函数所需要的值,结果就比较奇怪。
在这个例子中,这个空对象,就会呆在原来的位置上,剩余的将会正常排序。
有一些方案可以解决,但重要的是知道会发生这种情况。
在我的案例中,先过滤掉那些值为 undefined 的对象,然后在进行正常排序。
1objectArray2 .filter(obj => typeof obj.value !== 'undefined')3 .sort(compareObjects);4// => [ { value: 1 },5// { value: 3 },6// { value: 5 },7// { value: 7 },8// { value: 10 } ]
结语
以上这些可以看出排序方法并不是想象的那么简单。字符串能能很好的运行,数字需要一些输入和输出。
但如果在对象数组中碰到 null 或者 undefined ,我们一定要睁大眼睛时刻保持注意。
原文:https://hackernoon.com/how-not-to-sort-an-array-in-javascript-5t2a32av4