1

I am trying to sort an array of objects through one property. The array is getting sorted properly when I sort it using a compare function. But the order of the array is changing after the sort. What can I do to prevent it?.

I already thought of a solution, where I can separate the data:0 elements into an array and sort the remaining elements and concatenate both. Just wondering whether there is a simple way of doing it?

Note: hex property is a object which I can't use in the compare function. hex can have any value or string.

var arr = [
  {hex: "100x213123", data: 0},
  {hex: "20x213223", data: 0},
  {hex: "30x313123", data: 200},
  {hex: "40x413123", data: 0},
  {hex: "50x213123", data: 0},
  {hex: "60x213123", data: 0},
  {hex: "70x213123", data: 100},
  {hex: "80x213123", data: 0},
  {hex: "90x213123", data: 100},
  {hex: "100x213123",data: 100},
  {hex: "110x213123", data: 0},
  {hex: "120x213123", data: 0}
].sort(function (item1,item2){
     return item1.data - item2.data;
});
console.log(JSON.stringify(arr));

Result

[{"hex":"100x213123","data":0},
 {"hex":"120x213123","data":0},
 {"hex":"110x213123","data":0},
 {"hex":"40x413123","data":0},
 {"hex":"50x213123","data":0},
 {"hex":"60x213123","data":0},
 {"hex":"20x213223","data":0},
 {"hex":"80x213123","data":0},
 {"hex":"90x213123","data":100},
 {"hex":"100x213123","data":100},
 {"hex":"70x213123","data":100},
 {"hex":"30x313123","data":200}]

Expected

  [{hex: "100x213123", data: 0},
  {hex: "20x213223", data: 0},
  {hex: "40x413123", data: 0},
  {hex: "50x213123", data: 0},
  {hex: "60x213123", data: 0}, 
  {hex: "80x213123", data: 0},
  {hex: "110x213123", data: 0},
  {hex: "120x213123", data: 0},
  {hex: "70x213123", data: 100},
  {hex: "90x213123", data: 100},
  {hex: "100x213123",data: 100},
  {hex: "30x313123", data: 200}]

Any Ideas?

Sano
  • 108
  • 7
  • 3
    `But the order of the array is changing after the sort` - that's what sorting does - it changes the order of the array – Jaromanda X Jan 04 '17 at 01:24
  • It just looks like you are missing a secondary sort on the hex values. – Heinrich Jan 04 '17 at 01:25
  • @JaromandaX: Sorry I know that the order will change after sort. But I just want to have the same order for known values, like {hex: "10x213123", data: 0}, {hex: "20x213223", data: 0}. its already sorted can't I have the same ordering here? – Sano Jan 04 '17 at 01:28
  • @Sano Check my answer and tell me if it works? – Praveen Kumar Purushothaman Jan 04 '17 at 01:28
  • @PraveenKumar thanks for the quick answer. But thats not what I am aiming for. I don't want to sort values based on the hex property. Hex property can have any value it need not be like the example I have pasted. The first 2 values can be {hex: "1000x213123", data: 0}, {hex: "200x213223", data: 0}, and output should be in the same order – Sano Jan 04 '17 at 01:33
  • One idea is to label the initial positions with an `index` `{hex: "10x213123", data: 0, index: 0}, {hex: "20x213223", data: 0, index: 1}, ...`. Then sort by `index` after sorting by `data`. You can then remove the `index` property (*as in the function that does it adds and removes it*). – Spencer Wieczorek Jan 04 '17 at 01:33
  • @SpencerWieczorek: But wont the entire data be sorted again by index in that case?. – Sano Jan 04 '17 at 01:37
  • @Sano Updated. No, it won't be sorted entirely. – Praveen Kumar Purushothaman Jan 04 '17 at 01:37
  • @SpencerWieczorek Thanks – Sano Jan 04 '17 at 01:40
  • @PraveenKumar thanks – Sano Jan 04 '17 at 01:40
  • @Sano You are welcome... – Praveen Kumar Purushothaman Jan 04 '17 at 01:41

3 Answers3

4

You need to have a secondary sort if the data is same and sort on the hex values:

var arr = [
  {hex: "10x213123", data: 0},
  {hex: "20x213223", data: 0},
  {hex: "30x313123", data: 200},
  {hex: "40x413123", data: 0},
  {hex: "50x213123", data: 0},
  {hex: "60x213123", data: 0},
  {hex: "70x213123", data: 100},
  {hex: "80x213123", data: 0},
  {hex: "90x213123", data: 100},
  {hex: "100x213123",data: 100},
  {hex: "110x213123", data: 0},
  {hex: "120x213123", data: 0}
].sort(function(item1, item2) {
  if (item1.data !== item2.data)
    return item1.data - item2.data;
  else
    return item1.hex.split("x")[0] - item2.hex.split("x")[0]
});
console.log(JSON.stringify(arr));

So the best way is to add an index and then sort it as the second key.

var arr = [
  {hex: "10x213123", data: 0},
  {hex: "20x213223", data: 0},
  {hex: "30x313123", data: 200},
  {hex: "40x413123", data: 0},
  {hex: "50x213123", data: 0},
  {hex: "60x213123", data: 0},
  {hex: "70x213123", data: 100},
  {hex: "80x213123", data: 0},
  {hex: "90x213123", data: 100},
  {hex: "100x213123",data: 100},
  {hex: "110x213123", data: 0},
  {hex: "120x213123", data: 0}
].map(function (v, i) {
  v.index = i;
  return v;
}).sort(function(item1, item2) {
  if (item1.data !== item2.data)
    return item1.data - item2.data;
  else
    return item1.index - item2.index
}).map(function (v) {
  delete v.index;
  return v;
});
console.log(JSON.stringify(arr));

And remove the index.

Praveen Kumar Purushothaman
  • 164,888
  • 24
  • 203
  • 252
1

This behavior happens because Javascript's Array.sort is not guaranteed to be stable. This Stack Overflow question explores which browsers implements Javascript's Array.sort as a stable implementation, and points to a quick solution on how to convert an unstable sort to a stable one.

In general, if you want to avoid adding an "old index" field (or are unable to), you will want to have a copy of the array that you are sorting - this lets you sort by the sorter's comparator values first, then to sort by their old index if the comparison function determines that the two elements are equal.

For example, you might consider something like this:

function stable_sort(original_array) {
  var shallow_copy_array = original_array.slice();
  original_array.sort(function(left_element, right_element) {
    var comparison_result = left_element.data - right_element.data;
    if (comparison_result === 0) {
      return shallow_copy_array.indexOf(left_element) - shallow_copy_array.indexOf(right_element);
    } else {
      return comparison_result;
    }
  });
}
Community
  • 1
  • 1
KnightNiwrem
  • 56
  • 1
  • 3
0
.sort(function (item1,item2){
 if (item1.data > item2.data) {
     return 1;
 } else if (item1.data < item2.data) 
 { 
     return -1;
 } else if (item1.hex > item2.hex) {
     return 1;
 } else if (item1.hex < item2.hex) {
     return -1;
 } else {
     return 0;
 }

});

Joe Bonds
  • 104
  • 1
  • 5