如何不对 JavaScript 中的数组进行排序
数组排序是那种你不会花太多时间思考的事情,直到它停止工作。最近,我在 JavaScript 中处理数组元素时,发现它们排序完全不正确,界面也完全乱了。我花了很长时间才弄清楚到底出了什么问题,所以我想分享一下发生了什么,以及为什么它这么奇怪。
基本排序
JavaScript 中有一个sort
数组对象的方法,运行它应该能得到你期望的结果。例如:
const stringArray = ['cat', 'dog', 'ant', 'butterfly'];
stringArray.sort();
// => ['ant', 'butterfly', 'cat', 'dog']
如果你对可能包含成员的数组进行排序,那么它甚至会表现得相当不错undefined
。MDN说“所有未定义的元素都会排序到数组的末尾。”。
const stringArrayWithUndefined = [
'cat',
undefined,
'dog',
undefined,
'ant',
'butterfly',
'zebra'
];
stringArrayWithUndefined.sort();
// => ['ant', 'butterfly', 'cat', 'dog', 'zebra', undefined, undefined]
陷阱
您可能会遇到的第一个问题是,如果您发现自己有一个包含的数组null
。
const stringArrayWithUndefinedAndNull = [
'cat',
undefined,
'dog',
undefined,
'ant',
null,
'butterfly',
'zebra'
];
stringArrayWithUndefinedAndNull.sort();
// => ['ant', 'butterfly', 'cat', 'dog', null, 'zebra', undefined, undefined]
排序将强制转换为出现在字母表中间某处的null
字符串。"null"
然后是数字。JavaScript 默认的排序算法是将数组的所有成员转换为字符串,然后比较它们的 UTF-16 码元值序列。正如我们已经看到的,这种方法对于字符串数组非常有效,但对于数字来说,它很快就会失效。
const numberArray = [5, 3, 7, 1];
numberArray.sort();
// => [1, 3, 5, 7]
const biggerNumberArray = [5, 3, 10, 7, 1];
biggerNumberArray.sort();
// => [1, 10, 3, 5, 7]
在上面的例子中,10 排在 3 之前,因为“10”排在“3”之前。
我们可以通过为 JavaScript 提供一个用于排序的比较函数来解决这个问题。该函数从数组中接收两个元素,并返回一个数值,该数值是大于、小于还是等于零,决定了元素之间的相对排序方式。如果返回值小于零,则第一个元素排在第二个元素之前;如果返回值大于零,则第二个元素排在第一个元素之前。如果返回值为 0,则元素之间的相对顺序保持不变。
要按升序对数字进行排序,比较函数相对简单:
const compareNumbers = (a, b) => a - b;
从第二项中减去第一项即可满足上述要求。将此比较函数与我们biggerNumberArray
之前的函数一起使用,可以正确对数字进行排序。
biggerNumberArray.sort(compareNumbers);
// => [1, 3, 5, 7, 10]
如果您有元素,这仍然有效,undefined
因为它们会被忽略并排序到最后。
const numberArrayWithUndefined = [5, undefined, 3, 10, 7, 1];
numberArrayWithUndefined.sort(compareNumbers);
// => [1, 3, 5, 7, 10, undefined]
null
但又会引起问题。
const numberArrayWithUndefinedAndNull = [5, undefined, 3, null, 10, 7, 1];
numberArrayWithUndefinedAndNull.sort(compareNumbers);
// => [null, 1, 3, 5, 7, 10, undefined]
发生这种情况是因为强制转换null
为数字会返回 0。
Number(null);
// => 0
您可以在您的函数中处理这个问题compareNumbers
,或者对它的一致性感到满意。
不一致的陷阱
最大的问题,也是最近困扰我的,是当undefined
另一种方式潜入时。正如我们所见,如果数组包含undefined
它,它会被忽略并直接排到后面。然而,如果你对键可能存在的对象进行排序,undefined
这种自动排序就不会发生,结果会变得不一致。
例如,如果您有一个对象数组,其中一些对象有值,而一些对象没有值,则尝试按该值排序将不会得到您想要的结果。
const objectArray = [
{ value: 1 },
{ value: 10 },
{},
{ value: 5 },
{ value: 7 },
{ value: 3 }
];
const compareObjects = (a, b) => a.value - b.value;
objectArray.sort(compareObjects);
// => [ { value: 1 },
// { value: 10 },
// {},
// { value: 3 },
// { value: 5 },
// { value: 7 } ]
从一个数字中减去一个数字undefined
,或者undefined
从一个数字中减去一个数字,都会返回结果。NaN
由于这与比较函数所需的数字范围无关,因此sort
结果会有些奇怪。在这种情况下,导致问题的项会保留在数组的起始位置,而其他对象则会进行本地排序。
有几种方法可以解决这个问题,但重要的是要知道这种情况可能发生。就我而言,当我遇到这种情况时,我会过滤掉那些没有价值的项目,因为它们在变得重要之前并不重要。
objectArray.filter(obj => typeof obj.value !== 'undefined').sort(compareObjects);
// => [ { value: 1 },
// { value: 3 },
// { value: 5 },
// { value: 7 },
// { value: 10 } ]
注意分类
所有这些的结果是,这个sort
函数并不像看起来那么简单。字符串可以正常工作,数字需要一些输入,而且当它undefined
被处理为原语时,你必须注意强制转换null
s 或undefined
对象值。
你在用 JavaScript 或其他语言排序时遇到过问题吗?我也很想听听你的故事,可以在 Twitter 上关注我:@philnash。
文章来源:https://dev.to/philnash/how-not-to-sort-an-array-in-javascript-l9o