简体   繁体   English

Chrome 和 Firefox 中 Array.sort(comparisonFunction) 的不同结果

[英]Different result with Array.sort(comparisonFunction) in Chrome and Firefox

I am using anArrayOfObjects.sort((a, b) => a.value - b.value) , where some objects don't have a value property.我正在使用anArrayOfObjects.sort((a, b) => a.value - b.value) ,其中一些对象没有value属性。

That leads to different results in Firefox and Chrome, where Chrome seems to sort the object(s) with no value property/undefined value to the end, Firefox doesn't.这导致 Firefox 和 Chrome 中的结果不同,其中 Chrome 似乎将没有value属性/未定义值的对象排序到最后,Firefox 没有。

Is the spec not prescribing the result that Chrome gives, meaning the Firefox result is wrong?规范是否没有规定 Chrome 给出的结果,这意味着 Firefox 结果是错误的? Or is that part of the sort result up to a particular implementation?或者这部分sort结果取决于特定的实现?

 const data2 = [ { 'name': 'a', 'value': 5 }, { 'name': 'b', 'value': 2 }, { 'name': 'c' }, { 'name': 'd', 'value': 1 } ]; console.log('before sorting: ', data2); data2.sort((a, b) => a.value - b.value); console.log('after sorting: ', data2);

Neither are "wrong".两者都不是“错误的”。

undefined - undefined , undefined - 1 and 1 - undefined all return NaN , and NaN compared to something is always false . undefined - undefinedundefined - 11 - undefined都返回NaN ,与NaN相比,某些东西总是false的。

The difference between the 2 browsers is probably due to sorting implementation.两种浏览器之间的差异可能是由于排序实现。
The used sorting algorithm can give different results, depending on the order of values beforehand, and how the implementation deals with NaN .使用的排序算法可以给出不同的结果,具体取决于预先值的顺序,以及实现如何处理NaN

Don't allow browsers engines to decide how to handle your code in doubtful situations, just write your code without it:不要让浏览器引擎决定如何在有疑问的情况下处理您的代码,只需编写没有它的代码:

data2.sort((a, b) => (a?.value || 0) - (b?.value || 0));

(Default value could be some small or big number to sort with order you need.) (默认值可以是一些小的或大的数字,以便按照您需要的顺序进行排序。)

The compare function works as follows:比较 function 的工作原理如下:

compareFn(a, b) return value compareFn(a, b)返回值 sort order排序
< 0 sort a after b a排在b之后
> 0 sort a before bb之前排序a
=== 0 keep original order of a and b保持ab的原始顺序

Any subtractions involving undefined give a return value of NaN , which per the spec , is immediately converted to 0 .任何涉及undefined的减法都会返回NaN值, 根据规范,它会立即转换为0 However, the order will depend on the specific algorithm used by the implementation, as that determines which values get compared in this way.但是,顺序将取决于实现使用的特定算法,因为它决定了以这种方式比较哪些值。

The issue becomes clearer when you log out the comparisons:当您注销比较时,问题变得更加清晰:

 const data2 = [ { 'name': 'a', 'value': 5 }, { 'name': 'b', 'value': 2 }, { 'name': 'c' }, { 'name': 'd', 'value': 1 } ]; console.log('before sorting: ', data2); data2.sort((a, b) => (console.log(a.value, b.value), a.value - b.value)); console.log('after sorting: ', data2);

  • Firefox seems to be asking > 0 : Firefox似乎在问> 0

     5 2 5 undefined undefined 1
    1. a is 'a' and b is 'b' , return value is 3 so 'a' is must be after 'b' . a'a'并且b'b' ,返回值是3所以'a'必须在'b'之后。
    2. a is 'a' and b is 'c' , return value is 0 so 'a' stays before 'c' . a'a'b'c' ,返回值为0所以'a'停留在'c'之前。
    3. a is 'c' and b is 'd' , return value is 0 so 'c' stays before 'd' . a'c'b'd' ,返回值为0所以'c'停留在'd'之前。
  • Chrome seems to be asking < 0 : Chrome似乎在问< 0

     2 5 undefined 2 undefined 5 1 5 1 2
    1. a is 'b' and b is 'a' , return value is -3 so 'b' must be before 'a' . a'b'并且b'a' ,返回值为-3所以'b'必须在'a'之前。
    2. a is 'c' and b is 'b' , return value is 0 so 'c' stays after 'b' a'c'b'b' ,返回值为0所以'c'停留在'b'之后
    3. a is 'c' and b is 'a' , return value is 0 so 'c' stays after 'a' a'c'并且b'a' ,返回值为0所以'c'停留在'a'之后
    4. a is 'd' and b is 'a' , return value is -4 so 'd' must be before 'a' a'd'并且b'a' ,返回值为-4所以'd'必须在'a'之前
    5. a is 'd' and b is 'b' , return value is -1 so 'd' must be before 'b' a'd'并且b'b' ,返回值为-1所以'd'必须在'b'之前

Specifically Chrome doesn't ever compare 'c' and 'd' directly, so is never told to keep them in the same order.具体来说,Chrome 从来没有直接比较'c''d' ,所以从来没有被告知要让它们保持相同的顺序。

It is widely known that sorting algorithms are browsers implementation specific, moreover, since there are no silver bullet algorithms, a certain browser may use different sorting algorithms for different data shapes as a optimization technique.众所周知,排序算法是特定于浏览器实现的,此外,由于没有银弹算法,某个浏览器可能会针对不同的数据形状使用不同的排序算法作为优化技术。
This became obvious when looking at the following example results in Firefox:当查看 Firefox 中的以下示例结果时,这一点变得很明显:

 const arr1 = [ { 'value': 5 }, { 'value': 2 }, { 'value': undefined }, { 'value': 4 }, { 'value': 3 }, { 'value': undefined }, { 'value': 1 }, { 'value': 0 } ]; const arr2 = [5, 2, undefined, 4, 3, undefined, 1, 0]; arr1.sort((a, b) => a.value - b.value); arr2.sort((a, b) => a - b); console.log(arr1.map(v => `${v.value}`).join()); console.log(arr2.map(v => `${v}`).join());

In fact, Chrome results for the above examples are compliant with the specs :事实上,上述示例的 Chrome 结果符合规范

  1. Let SortCompare be a new Abstract Closure with parameters (x, y) that captures comparefn and performs the following steps when called:让 SortCompare 成为一个带有参数 (x, y) 的新抽象闭包,它捕获 comparefn 并在调用时执行以下步骤:
    a.一个。 If x and y are both undefined, return +0.如果 x 和 y 都未定义,则返回 +0。
    b.湾。 If x is undefined, return 1.如果 x 未定义,则返回 1。
    c. c。 If y is undefined, return -1.如果 y 未定义,则返回 -1。

From the above statement can be concluded that undefined values always compares greater than any other value, that's why they are sorted at the end.从上面的陈述可以得出结论, undefined的值总是比较大于任何其他值,这就是它们最后排序的原因。
This result can be achieved with a merge sort algorithm with special handling of undefined values, below is a pseudo-code of this:这个结果可以通过对未定义值进行特殊处理的merge sort算法来实现,下面是它的伪代码:

 const mergeSort = arr => { let voids = 0; const merge = (l, r) => { const out = []; l.length && l[0] === undefined && (l.shift(), voids++); while (l.length && r.length) out.push(l[0] <= r[0]? l.shift(): r.shift()); while(l.length) out.push(l.shift()); while(r.length) out.push(r.shift()); return out; }; const sort = arr => { if (arr.length < 2) return arr; const m = Math.floor(arr.length / 2); const l = arr.slice(0, m), r = arr.slice(m); return merge(sort(l), sort(r)); }; const result = sort(arr); while (voids--) result.push(undefined); return result; }; const arr = [5, 2, undefined, 4, 3, undefined, 1, 0]; console.log(mergeSort(arr).map(v => `${v}`).join());

About the sorted array of objects result in Firefox, in the first example, it can be obtained with a simple insertion sort algorithm, as below:关于排序后的对象数组Firefox,在第一个例子中,可以通过简单的insertion sort算法得到,如下:

 const insertionSort = arr => { for (let i = 1; i < arr.length; i++) { let x = i - 1, n = arr[i]; for (; x >= 0 && arr[x] > n; x--) arr[x + 1] = arr[x]; arr[x + 1] = n; } return arr; }; const arr = [5, 2, undefined, 4, 3, undefined, 1, 0]; console.log(insertionSort(arr).map(v => `${v}`).join());

@jonrsharpe maa kyu chuda rha jab sab pta h phle se @jonrsharpe maa kyu chuda rha jab sab pta h phle se

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

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