简体   繁体   English

JS - 为什么 array.sort() 不会为对象和数字返回相同的结果?

[英]JS - Why array.sort() doesn't return the same result for objects and numbers?

Sorting an array of objects (by a property of type number) doesn't return a sorted result like for an array of numbers.对对象数组进行排序(按数字类型的属性)不会像数字数组那样返回排序结果。
Why so?为什么这样?
How to make it sort like for numbers?如何使它像数字一样排序?

Demo: Sorting an array of numbers演示:对数字数组进行排序

 const sorted = [0, 5, 2, undefined, 3, 1, 4].sort((a, b) => a - b); console.log(sorted);

Demo: Sorting an array of objects演示:对对象数组进行排序

 const notSorted = [ {i:0}, {i:5}, {i:2}, {i: undefined}, {i:3}, {i:1}, {i:4}, ].sort((a, b) => ai - bi).map(a => ai); console.log(notSorted);

I am currently on Chrome 90. Maybe some other browsers or engines don't have this issue.我目前使用的是 Chrome 90。也许其他一些浏览器或引擎没有这个问题。 Tell me.告诉我。

According to the spec :根据规范

  • If x and y are both undefined, return +0.如果 x 和 y 都未定义,则返回 +0。
  • If x is undefined, return 1.如果 x 未定义,则返回 1。
  • If y is undefined, return −1.如果 y 未定义,则返回 -1。
  • If the argument comparefn is not undefined, then如果参数 comparefn 不是未定义的,则
    • Let v be ToNumber(Call(comparefn, undefined, «x, y»)).令 v 为 ToNumber(Call(comparefn, undefined, «x, y»))。
    • ReturnIfAbrupt(v). ReturnIfAbrupt(v)。
    • If v is NaN, return +0.如果 v 是 NaN,则返回 +0。
    • Return v.返回 v.

That explains why it works in the first case because the sorted values are not enclosed in an object.这就解释了为什么它在第一种情况下有效,因为排序值没有包含在 object 中。 In the second case the values are not undefined (only the properties are) so the native undefined handling of Array.prototype.sort() doesn't take over, which means the callback is being executed even if ai or bi is undefined , and it returns NaN (not a number).在第二种情况下,值不是undefined的(只有属性是),因此Array.prototype.sort()的本机undefined处理不会接管,这意味着即使aibi undefined ,也会执行回调,并且它返回NaN (不是数字)。

As the callback returns NaN for every undefined property, they are considered equal to every other item.由于回调为每个undefined的属性返回NaN ,因此它们被认为与其他所有项相等。 That leads to an erratic behavior, which depends on the actual algorithm of Array.prototype.sort() in the JavaScript engine.这会导致不稳定的行为,这取决于 JavaScript 引擎中Array.prototype.sort()的实际算法。

Here are the return values of the question example for some browsers:以下是某些浏览器的问题示例的返回值:

  • IE 11: [0, 1, 2, 5, undefined, 3, 4] IE 11: [0, 1, 2, 5, undefined, 3, 4]
  • Edge Chromium 90: [0, 1, 2, 3, 5, undefined, 4] Edge Chromium 90: [0, 1, 2, 3, 5, undefined, 4]
  • Firefox 88: [0, 2, 5, undefined, 1, 3, 4] Firefox 88: [0, 2, 5, undefined, 1, 3, 4]

Your sorting algorithm produces, in some instances, NaN , since undefined - someNum and someNum - undefined both result in NaN .在某些情况下,您的排序算法会产生NaN ,因为undefined - someNumsomeNum - undefined都导致NaN This means that your callback is not consistent , which means that the resulting sort order is implementation-defined.这意味着您的回调不一致,这意味着生成的排序顺序是实现定义的。

A function comparefn is a consistent comparison function for a set of values S if all of the requirements below are met for all values a, b, and c (possibly the same value) in the set S: The notation a <CF b means comparefn(a, b) < 0; A function comparefn is a consistent comparison function for a set of values S if all of the requirements below are met for all values a, b, and c (possibly the same value) in the set S: The notation a <CF b means comparefn (a, b) < 0; a =CF b means comparefn(a, b) = 0 (of either sign); a =CF b 表示 comparefn(a, b) = 0(任一符号); and a >CF b means comparefn(a, b) > 0. a >CF b 表示 comparefn(a, b) > 0。

  • Calling comparefn(a, b) always returns the same value v when given a specific pair of values a and b as its two arguments.当给定一对特定的值 a 和 b 作为它的两个 arguments 时,调用 comparefn(a, b) 总是返回相同的值 v。 Furthermore, Type(v) is Number, and v is not NaN.此外, Type(v) 是 Number,而 v 不是 NaN。 Note that this implies that exactly one of a <CF b, a =CF b, and a >CF b will be true for a given pair of a and b.请注意,这意味着对于给定的 a 和 b 对,a <CF b、a =CF b 和 a >CF b 中的一个将是正确的。

If you ever return NaN from a .sort callback, your results can be anything at all: the behavior in such a case is undefined by the specification (though certain implementations might produce a result that makes more intuitive sense... or not).如果你曾经从.sort回调中返回NaN ,你的结果可以是任何东西:这种情况下的行为是规范未定义的(尽管某些实现可能会产生更直观的结果......或者不是)。 So, make sure never to return NaN .因此,请确保永远不要返回NaN In this case, explicitly test to see if the .i property being iterated over is undefined , and substitute a different value for it - maybe Infinity or -Infinity.在这种情况下,显式测试以查看被迭代的.i属性是否为undefined ,并为其替换一个不同的值——可能是 Infinity 或 -Infinity。

 const sanitize = val => val === undefined? Infinity: val; const notSorted = [ {i:0}, {i:5}, {i:2}, {i: undefined}, {i:3}, {i:1}, {i:4}, ].sort((a, b) => sanitize(ai) - sanitize(bi)).map(a => ai); console.log(notSorted);

Because you got an object with an undefined property in that array, on which your comparison function is not consistent .因为你得到了一个 object 在该数组中有一个undefined的属性,你的比较 function 是不一致的 You'll need to ensure that it returns a number, not NaN .您需要确保它返回一个数字,而不是NaN Chrome uses different algorithms for sorting number arrays vs object arrays, and that you got lucky in one case doesn't mean it would always work. Chrome 使用不同的算法对数字 arrays 与 object arrays 进行排序,在一种情况下你很幸运并不意味着它总是有效。 It does work as expected with the array of plain numbers, since .sort() ignores undefined array elements (not attempting to compare them against something else) and always puts them at the end of the array.它确实可以按预期使用纯数字数组,因为.sort()忽略undefined的数组元素(不尝试将它们与其他元素进行比较)并始终将它们放在数组的末尾。

You can fix it by doing你可以通过这样做来修复它

.sort((a, b) => (a.i ?? -Infinity) - (b.i ?? -Infinity))

(or +Infinity , depending on whether you want your undefined values first or last). (或+Infinity ,取决于您是首先还是最后想要undefined的值)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM