Chrome Array.sorting numbers out of order

浪子不回头ぞ 提交于 2020-01-24 20:58:06

问题


I was trying to sort an array of divs so that if a is below or to the left of b, a is before b.

After a couple hours in CodePen, I realized that if an array is ten or more items in length, Chrome will sort the items out of order, at least with this compare function:

var array = [0,1,2,3,4,5,6,7,8,9,10];
array.sort(function(a, b) {
        return -1;
});

Chrome returns:

[0, 2, 3, 4, 1, 6, 7, 8, 9, 10, 5]

See on CodePen

If you log a and b inside the sort function, it becomes obvious why it happens--it's just the algorithm Chrome uses. I know people use return a-b etc ... but let's turn to the following function ... The array is of jQuery objects containing divs. I want a to come before b if a is below or to the left of b. Any help??

EDIT: In response to some answers here, I rewrite function to output either 1, -1, or 0. Still, I get unwanted results. See how in the output, the first object's right property is greater than the second's left property, and the first object's top property is lower than the second's bottom. According to the compare function, they should be in the opposite order.

var array = [
  
  {
    bottom:1181.8854675292969,
    left:23.39583396911621,
    right:72.39583396911621,
    top:910.8854675292969,
  },
  
  {
    bottom:1181.3750305175781,
    left:78.39583587646484,
    right:183.39583587646484,
    top:1132.3750305175781
  },
  
  {
    bottom:1182.6042175292969,
    left:189.39584350585938,
    right:349.3958435058594,
    top:1021.6042175292969
  },
  
  {
    bottom:1181.3750305175781,
    left:355.3958435058594,
    right:626.3958435058594,
    top:1132.3750305175781
  },
   
  {
    bottom:1133.2292175292969,
    left:355.3958435058594,
    right:632.3958435058594,
    top:1132.2292175292969
  },
  
  {
    bottom:1127.0208435058594,
    left:78.39583587646484,
    right:183.39583587646484,
    top:1022.0208435058594
  },
  
  {
    bottom:1127.0208435058594,
    left:355.3958435058594,
    right:460.3958435058594,
    top:1022.0208435058594
  },
  
  {
    bottom:1127.0208435058594,
    left:466.3958435058594,
    right:571.3958435058594,
    top:1022.0208435058594,
  },
  
  {
    bottom:1016.0208435058594,
    left:78.39583587646484,
    right:183.39583587646484,
    top:911.0208435058594
  },
  
  {
    bottom:1016.2395935058594,
    left:189.39584350585938,
    right:515.3958435058594,
    top:800.2395935058594
  },
   
  {
    bottom:1016.2395935058594,
    left:521.3958740234375,
    right:626.3958740234375,
    top:800.2395935058594
  },  
  
  {
    bottom:906.0208435058594,
    left:23.39583396911621,
    right:183.3958339691162,
    top:801.0208435058594
  },
  
  {
    bottom:794.6041870117188,
    left:23.39583396911621,
    right:72.39583396911621,
    top:634.6041870117188
  },
  
  {
    bottom:795.0208435058594,
    left:78.39583587646484,
    right:183.39583587646484,
    top:690.0208435058594
  },
  
  {
    bottom:794.0208435058594,
    left:189.39584350585938,
    right:404.3958435058594,
    top:689.0208435058594
  },
  
  {
    bottom:794.0208435058594,
    left:410.3958435058594,
    right:515.3958435058594,
    top:689.0208435058594
  },
  
  {
    bottom:794.0208435058594,
    left:521.3958740234375,
    right:626.3958740234375,
    top:689.0208435058594
  },
  
  {
    bottom:683.3750152587891,
    left:78.39583587646484,
    right:183.39583587646484,
    top:634.3750152587891
  },
  
  {
    bottom:684.6041870117188,
    left:189.39584350585938,
    right:349.3958435058594,
    top:523.6041870117188
  },
  
  {
    bottom:684.6041870117188,
    left:355.3958435058594,
    right:570.3958435058594,
    top:523.6041870117188
  },
  
  {
    bottom:629.0208435058594,
    left:23.39583396911621,
    right:183.3958339691162,
    top:524.0208435058594
  },
    
  {
    bottom:518.2395935058594,
    left:23.39583396911621,
    right:128.3958339691162,
    top:302.2395935058594
  },
  
  {
    bottom:517.8854217529297,
    left:134.39584350585938,
    right:405.3958435058594,
    top:246.8854217529297
  },
  
  {
    bottom:518.604175567627,
    left:411.3958435058594,
    right:626.3958435058594,
    top:357.60417556762695
  }
];

array.sort(function(a, b) {
  if(a.bottom < b.top || a.left > b.right)
	  return 1;
	if(a.bottom > b.top || a.left < b.right)
		return -1;
	return 0;
});

console.log(array[4],array[8]);

EDIT: Found a workaround for my purposes. I used forEach to compare the items to each other and increment z-index based on vertical and then horizontal order:

function setTileZIndex() {
        var $tiles = $('.grid__item__wrap');
        var coords = [];
        $tiles.each(function(index) {
            var topLeft = $(this).offset();
            var obj = {
                bottom: topLeft.top + $(this).height(),
                left: topLeft.left,
                top: topLeft.top,
                right: topLeft.left + $(this).width(),
                $this: $(this),
                z: 9999
            };
            coords.push(obj);
        });

        coords.forEach(function(a) {
            coords.forEach(function(b) {
                if (a.bottom < b.top)
                    b.z += 4;
                if (a.left > b.right)
                    b.z += 1;
            })
        });

        coords.forEach(function(elt) {
            elt.$this.css('z-index', elt.z);
        });
    }

回答1:


Your compare function must return:

  • negative: when the first element should come before the second
  • zero: when the order between elements doesn't matter
  • positive: when the second element should come before the first.

Returning always -1 causes a random result.

I'm afraid it is not possible to do what you are trying to do, because the compare function must be consistent through all elements in the array. With the compare function you are using, it is possible to have f(a, b) = -1, and f(b, a) = -1, which is inconsistent: either a or b should come first.




回答2:


EDIT

It seems you've over-simplified in your original question. Here's an updated answer:

I want a to come before b if a is below or to the left of b. Any help??

In that case, make sure you're comparing the same edges of each object – ie compare a.left with b.left, and a.bottom with b.bottom ...

const data = [
  { bottom:1181, left:23, right:72, top:910, },
  { bottom:906, left:23, right:183, top:801 },
  { bottom:1181, left:78, right:183, top:1132 },
  { bottom:1182, left:189, right:349, top:1021 },
  { bottom:1133, left:355, right:632, top:1132 },
  { bottom:795, left:78, right:183, top:690 },
  { bottom:1181, left:355, right:626, top:1132 },
  { bottom:1127, left:78, right:183, top:1022 },
  { bottom:1127, left:355, right:460, top:1022 },
  { bottom:1127, left:466, right:571, top:1022, },
  { bottom:1016, left:78, right:183, top:911 },
]

data.sort((a,b) => {
  if (a.left < b.left || a.bottom < b.bottom)
    return -1
  else if (a.right > b.right || a.top > b.top)
    return 1
  else
    return 0
})

console.log(data)
// [ { bottom: 906, left: 23, right: 183, top: 801 },
//   { bottom: 1181, left: 23, right: 72, top: 910 },
//   { bottom: 795, left: 78, right: 183, top: 690 },
//   { bottom: 1016, left: 78, right: 183, top: 911 },
//   { bottom: 1127, left: 78, right: 183, top: 1022 },
//   { bottom: 1182, left: 189, right: 349, top: 1021 },
//   { bottom: 1133, left: 355, right: 632, top: 1132 },
//   { bottom: 1181, left: 78, right: 183, top: 1132 },
//   { bottom: 1127, left: 355, right: 460, top: 1022 },
//   { bottom: 1181, left: 355, right: 626, top: 1132 },
//   { bottom: 1127, left: 466, right: 571, top: 1022 } ]

Original answer

I'm certain this has been answered somewhere else on this site, but your comparator must return -1, 0, and 1 values to get an intended result

  • -1 moves the a to the left of b
  • 1 moves the a to the right of b
  • 0 results in neither a or b changing positions

let sorted = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10].sort((a,b) => {
  if (a < b)
    return -1
  else if (a > b)
    return 1
  else
    return 0
})

console.log(sorted)
// [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]

Or use super-terse but harder-to-read chained ternary expressions

let sorted = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10].sort((a,b) =>
  a < b ? -1 : a > b ? 1 : 0
)

console.log(sorted)
// [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]

Keep in mind, elements in the array are not necessarily compared in an order you might expect – ie, don't expect compare(0,1) then compare(1,2), then compare(2,3), etc

let sorted = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10].sort((a,b) => {
  console.log(a,b)
  return a < b ? -1 : (a > b ? 1 : 0)
})
// 0 10
// 0 5
// 10 5
// 2 5
// 3 5
// 4 5
// 1 5
// 6 5
// 9 5
// 8 5
// 7 5
// 0 2
// 2 3
// 3 4
// 4 1
// 3 1
// 2 1
// 0 1
// 6 7
// 7 8
// 8 9
// 9 10

console.log(sorted)
//=> [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]



回答3:


Sorry, in my haste I probably didn't explain thoroughly. The only thing required of the resulting array is that, if a box is completely above OR to the right of another

(a.bottom < b.top || a.left > b.right)

it should come after the other box.

One set of conditions are

a.bottom < b.top || a.left > b.right ? 1 : -1

as verified by .reduceRight() call.

var coords = [
  
  {
    bottom:1181.8854675292969,
    left:23.39583396911621,
    right:72.39583396911621,
    top:910.8854675292969,
  },
  
  {
    bottom:1181.3750305175781,
    left:78.39583587646484,
    right:183.39583587646484,
    top:1132.3750305175781
  },
  
  {
    bottom:1182.6042175292969,
    left:189.39584350585938,
    right:349.3958435058594,
    top:1021.6042175292969
  },
  
  {
    bottom:1181.3750305175781,
    left:355.3958435058594,
    right:626.3958435058594,
    top:1132.3750305175781
  },
   
  {
    bottom:1133.2292175292969,
    left:355.3958435058594,
    right:632.3958435058594,
    top:1132.2292175292969
  },
  
  {
    bottom:1127.0208435058594,
    left:78.39583587646484,
    right:183.39583587646484,
    top:1022.0208435058594
  },
  
  {
    bottom:1127.0208435058594,
    left:355.3958435058594,
    right:460.3958435058594,
    top:1022.0208435058594
  },
  
  {
    bottom:1127.0208435058594,
    left:466.3958435058594,
    right:571.3958435058594,
    top:1022.0208435058594,
  },
  
  {
    bottom:1016.0208435058594,
    left:78.39583587646484,
    right:183.39583587646484,
    top:911.0208435058594
  },
  
  {
    bottom:1016.2395935058594,
    left:189.39584350585938,
    right:515.3958435058594,
    top:800.2395935058594
  },
   
  {
    bottom:1016.2395935058594,
    left:521.3958740234375,
    right:626.3958740234375,
    top:800.2395935058594
  },  
  
  {
    bottom:906.0208435058594,
    left:23.39583396911621,
    right:183.3958339691162,
    top:801.0208435058594
  },
  
  {
    bottom:794.6041870117188,
    left:23.39583396911621,
    right:72.39583396911621,
    top:634.6041870117188
  },
  
  {
    bottom:795.0208435058594,
    left:78.39583587646484,
    right:183.39583587646484,
    top:690.0208435058594
  },
  
  {
    bottom:794.0208435058594,
    left:189.39584350585938,
    right:404.3958435058594,
    top:689.0208435058594
  },
  
  {
    bottom:794.0208435058594,
    left:410.3958435058594,
    right:515.3958435058594,
    top:689.0208435058594
  },
  
  {
    bottom:794.0208435058594,
    left:521.3958740234375,
    right:626.3958740234375,
    top:689.0208435058594
  },
  
  {
    bottom:683.3750152587891,
    left:78.39583587646484,
    right:183.39583587646484,
    top:634.3750152587891
  },
  
  {
    bottom:684.6041870117188,
    left:189.39584350585938,
    right:349.3958435058594,
    top:523.6041870117188
  },
  
  {
    bottom:684.6041870117188,
    left:355.3958435058594,
    right:570.3958435058594,
    top:523.6041870117188
  },
  
  {
    bottom:629.0208435058594,
    left:23.39583396911621,
    right:183.3958339691162,
    top:524.0208435058594
  },
    
  {
    bottom:518.2395935058594,
    left:23.39583396911621,
    right:128.3958339691162,
    top:302.2395935058594
  },
  
  {
    bottom:517.8854217529297,
    left:134.39584350585938,
    right:405.3958435058594,
    top:246.8854217529297
  },
  
  {
    bottom:518.604175567627,
    left:411.3958435058594,
    right:626.3958435058594,
    top:357.60417556762695
  }
];


// a.bottom < b.top || a.left > b.right ? a.bottom > b.top || a.left < b.right ? 0 : 1 : -1
coords.sort((a, b) => a.bottom < b.top || a.left > b.right ? 1 : -1);

console.log(coords);

coords.reduceRight((a, b) => {console.log(a.bottom < b.top || a.left > b.right); return b});



回答4:


Here is a comparison function that will work for you.

var arr = [15, 4, 11, 19, 11];

function compareFn(a, b) {
    return a > b ? 1 : a === b ? 0 : -1;
}

var sorted = arr.sort(compareFn);

console.log(sorted);



回答5:


Please test the code in Chrome and for example in Edge. In Crome, the sort is not stable, if a return value is the same for all comparisons.

var array = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
array.sort(function(a, b) { return 0; });
console.log(array); // Chrome: [5, 0, 2, 3, 4, 1, 6, 7, 8, 9, 10]
                    // Edge:   [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

var array = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
array.sort(function(a, b) { return 1; });
console.log(array); // Chrome: [5, 10, 0, 9, 8, 7, 6, 1, 4, 3, 2]
                    // Edge:   [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

var array = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
array.sort(function(a, b) { return -1; });
console.log(array); // Chrome: [0, 2, 3, 4, 1, 6, 7, 8, 9, 10, 5]
                    // Edge:   [10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]


来源:https://stackoverflow.com/questions/43563981/chrome-array-sorting-numbers-out-of-order

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!