【问题标题】:JS - Why array.sort() doesn't return the same result for objects and numbers?JS - 为什么 array.sort() 不会为对象和数字返回相同的结果?
【发布时间】:2021-05-06 14:49:02
【问题描述】:

对对象数组进行排序(按数字类型的属性)不会像数字数组那样返回排序结果。
为什么会这样?
如何让它像数字一样排序?

演示:对数字数组进行排序

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

演示:对对象数组进行排序

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

我目前使用的是 Chrome 90。也许其他一些浏览器或引擎没有这个问题。告诉我。

【问题讨论】:

    标签: javascript sorting


    【解决方案1】:

    根据spec

    • 如果 x 和 y 都未定义,则返回 +0。
    • 如果 x 未定义,则返回 1。
    • 如果 y 未定义,则返回 -1。
    • 如果参数 comparefn 不是未定义的,则
      • 设 v 为 ToNumber(Call(comparefn, undefined, «x, y»))。
      • ReturnIfAbrupt(v)。
      • 如果 v 是 NaN,则返回 +0。
      • 返回诉

    这解释了为什么它在第一种情况下有效,因为排序后的值没有包含在对象中。在第二种情况下,值不是undefined(只有属性是),因此Array.prototype.sort() 的本机undefined 处理不会接管,这意味着即使a.ib.i 正在执行回调是undefined,它返回NaN(不是数字)。

    当回调为每个 undefined 属性返回 NaN 时,它们被视为与其他所有项目相同。这会导致不稳定的行为,这取决于 JavaScript 引擎中 Array.prototype.sort() 的实际算法。

    以下是一些浏览器的问题示例的返回值:

    • IE 11:[0, 1, 2, 5, undefined, 3, 4]
    • 边缘铬 90:[0, 1, 2, 3, 5, undefined, 4]
    • 火狐88:[0, 2, 5, undefined, 1, 3, 4]

    【讨论】:

    • 没错。通过使用另一个数组来存储实际的排序值(在排序内部),不会出现undefined
    【解决方案2】:

    在某些情况下,您的排序算法会产生NaN,因为undefined - someNumsomeNum - undefined 都会产生NaN。这意味着您的回调是not consistent,这意味着生成的排序顺序是实现定义的。

    如果集合 S 中的所有值 a、b 和 c(可能相同的值)都满足以下所有要求,则函数 comparefn 是一组值 S 的一致比较函数:符号 a CF b 表示 comparefn(a, b) > 0。

    • 当给定一对特定的值 a 和 b 作为其两个参数时,调用 comparefn(a, b) 总是返回相同的值 v。此外,Type(v) 是 Number,而 v 不是 NaN。 请注意,这意味着对于 a给定一对 a 和 b。

    如果你曾经从.sort 回调中返回NaN,你的结果可以是任何东西:这种情况下的行为是规范未定义(尽管某些实现可能会产生结果更直观……或者不是)。因此,请确保永远不要返回 NaN。在这种情况下,显式测试以查看被迭代的 .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(a.i) - sanitize(b.i))
      .map(a => a.i);
      
    console.log(notSorted);

    【讨论】:

      【解决方案3】:

      因为您在该数组中有一个具有undefined 属性的对象,而your comparison function is not consistent 在其上。您需要确保它返回一个数字,而不是 NaNChrome uses different algorithms 用于对数字数组与对象数组进行排序,在一种情况下你很幸运并不意味着它总是可以工作。 它确实可以与普通数字数组一样工作,因为@987654325 @ 忽略 undefined 数组元素(不尝试将它们与其他元素进行比较)并始终将它们放在数组的末尾。

      你可以通过这样做来解决它

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

      (或+Infinity,取决于您是希望undefined 值在前还是最后)。

      【讨论】:

      • 您链接的 V8 文章不是说他们现在终于通过仅使用 Timsort 解决了使用不同算法的问题吗?
      • @Kaiido 哎呀,你是对的。我认为Guerric有实际的答案。重读文章,关键句是“基本思想是将所有未定义的值收集到一个临时列表中,对这个临时列表进行排序,然后将排序后的值写回实际的数组或对象中。 i>”和“规范要求undefineds 必须排序到最后。除了undefined 的值实际上并没有传递给用户提供的比较函数,...”。跨度>
      猜你喜欢
      • 1970-01-01
      • 2017-04-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-10-12
      • 2010-10-31
      • 2013-03-13
      • 1970-01-01
      相关资源
      最近更新 更多