如何不对 JavaScript 中的数组进行排序

2025-06-07

如何不对 JavaScript 中的数组进行排序

数组排序是那种你不会花太多时间思考的事情,直到它停止工作。最近,我在 JavaScript 中处理数组元素时,发现它们排序完全不正确,界面也完全乱了。我花了很长时间才弄清楚到底出了什么问题,所以我想分享一下发生了什么,以及为什么它这么奇怪。

基本排序

JavaScript 中有一个sort数组对象的方法,运行它应该能得到你期望的结果。例如:

const stringArray = ['cat', 'dog', 'ant', 'butterfly'];
stringArray.sort();
// => ['ant', 'butterfly', 'cat', 'dog']

Enter fullscreen mode Exit fullscreen mode

如果你对可能包含成员的数组进行排序,那么它甚至会表现得相当不错undefined。MDN说“所有未定义的元素都会排序到数组的末尾。”

const stringArrayWithUndefined = [
  'cat',
  undefined,
  'dog',
  undefined,
  'ant',
  'butterfly',
  'zebra'
];
stringArrayWithUndefined.sort();
// => ['ant', 'butterfly', 'cat', 'dog', 'zebra', undefined, undefined]

Enter fullscreen mode Exit fullscreen mode

陷阱

您可能会遇到的第一个问题是,如果您发现自己有一个包含的数组null

const stringArrayWithUndefinedAndNull = [
  'cat',
  undefined,
  'dog',
  undefined,
  'ant',
  null,
  'butterfly',
  'zebra'
];
stringArrayWithUndefinedAndNull.sort();
// => ['ant', 'butterfly', 'cat', 'dog', null, 'zebra', undefined, undefined]

Enter fullscreen mode Exit fullscreen mode

排序将强制转换为出现在字母表中间某处的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]

Enter fullscreen mode Exit fullscreen mode

在上面的例子中,10 排在 3 之前,因为“10”排在“3”之前。

我们可以通过为 JavaScript 提供一个用于排序的比较函数来解决这个问题。该函数从数组中接收两个元素,并返回一个数值,该数值是大于、小于还是等于零,决定了元素之间的相对排序方式。如果返回值小于零,则第一个元素排在第二个元素之前;如果返回值大于零,则第二个元素排在第一个元素之前。如果返回值为 0,则元素之间的相对顺序保持不变。

要按升序对数字进行排序,比较函数相对简单:

const compareNumbers = (a, b) => a - b;

Enter fullscreen mode Exit fullscreen mode

从第二项中减去第一项即可满足上述要求。将此比较函数与我们biggerNumberArray之前的函数一起使用,可以正确对数字进行排序。

biggerNumberArray.sort(compareNumbers);
// => [1, 3, 5, 7, 10]

Enter fullscreen mode Exit fullscreen mode

如果您有元素,这仍然有效,undefined因为它们会被忽略并排序到最后。

const numberArrayWithUndefined = [5, undefined, 3, 10, 7, 1];
numberArrayWithUndefined.sort(compareNumbers);
// => [1, 3, 5, 7, 10, undefined]

Enter fullscreen mode Exit fullscreen mode

null但又会引起问题。

const numberArrayWithUndefinedAndNull = [5, undefined, 3, null, 10, 7, 1];
numberArrayWithUndefinedAndNull.sort(compareNumbers);
// => [null, 1, 3, 5, 7, 10, undefined]

Enter fullscreen mode Exit fullscreen mode

发生这种情况是因为强制转换null为数字会返回 0。

Number(null);
// => 0

Enter fullscreen mode Exit fullscreen mode

您可以在您的函数中处理这个问题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 } ]

Enter fullscreen mode Exit fullscreen mode

从一个数字中减去一个数字undefined,或者undefined从一个数字中减去一个数字,都会返回结果。NaN由于这与比较函数所需的数字范围无关,因此sort结果会有些奇怪。在这种情况下,导致问题的项会保留在数组的起始位置,而其他对象则会进行本地排序。

有几种方法可以解决这个问题,但重要的是要知道这种情况可能发生。就我而言,当我遇到这种情况时,我会过滤掉那些没有价值的项目,因为它们在变得重要之前并不重要。

objectArray.filter(obj => typeof obj.value !== 'undefined').sort(compareObjects);
// => [ { value: 1 },
// { value: 3 },
// { value: 5 },
// { value: 7 },
// { value: 10 } ]

Enter fullscreen mode Exit fullscreen mode

注意分类

所有这些的结果是,这个sort函数并不像看起来那么简单。字符串可以正常工作,数字需要一些输入,而且当它undefined被处理为原语时,你必须注意强制转换nulls 或undefined对象值。

你在用 JavaScript 或其他语言排序时遇到过问题吗?我也很想听听你的故事,可以在 Twitter 上关注我:@philnash

文章来源:https://dev.to/philnash/how-not-to-sort-an-array-in-javascript-l9o
PREV
我构建了一个 VSCode 扩展:ngrok for VSCode
NEXT
保持 fork 保持最新的 Git 命令提示