1

I have an array of objects example

var objects = [ 
    { first_nom: 'dave', value: '5'},
    { first_nom: 'roger', value: '-0'},
    { first_nom: 'pete', value: '+0' },
    { first_nom: 'pete', value: '-5' },
];

Sorting this by value would ideally reverse the two zeros.

I have tried the normal javascript/jquery and underscore sorts but they ignore the leading - or + and probably rightly so.

This is what I currently have (this sorts fine but ignores the + and -):

var chartData = _.sortBy(objects, 'value').reverse();

Is there anything else someone can recommend please?

Many thanks

Nev

georg
  • 211,518
  • 52
  • 313
  • 390
Nevnev
  • 13
  • 1
  • 6
  • You'll have to add it as a special case to the sorting function. Show what you have now and someone'll be able to tell how to change it. – JJJ Oct 31 '14 at 12:44
  • 3
    Don't use strings as value, but just an int. Int's can be negative – Andreas Furster Oct 31 '14 at 12:45
  • @AndreasFurster, that still won't sort them properly as it will convert the signed zeros to 'normal' zeros and nothing will happen. – Andy Oct 31 '14 at 12:47
  • 1
    Are the signed zeros really necessary for what you want to do? – Andy Oct 31 '14 at 12:48
  • Unfortunately yes. A total pain – Nevnev Oct 31 '14 at 12:49
  • I don't have access or rather it's not easy to access the ACTUAL value before this formatting was applied so I'm left with this data – Nevnev Oct 31 '14 at 12:50
  • @Andy The double precision floating point format distinguishes between positive and negative zeros which is why `1 / 0` is `Infinity` whereas `1 / -0` is `-Infinity`. See my answer below. – Aadit M Shah Oct 31 '14 at 13:13

3 Answers3

2

You can special-case them, sorting as strings when both values are 0:

var objects = [ 
    { first_nom: 'dave', value: '5'},
    { first_nom: 'roger', value: '-0'},
    { first_nom: 'pete', value: '+0' },
    { first_nom: 'pete', value: '-5' },
];

var sorted = objects.sort(
  function(a,b) {
    if ((a.value == 0) && (b.value == 0))
      {
        return b.value.localeCompare(a.value);
      }
    else
      return b.value - a.value;
  }
);

console.log(JSON.stringify(sorted));

Results in:

[
  {"first_nom":"dave","value":"5"},
  {"first_nom":"pete","value":"+0"},
  {"first_nom":"roger","value":"-0"},
  {"first_nom":"pete","value":"-5"}
] 
Paul Roub
  • 36,322
  • 27
  • 84
  • 93
  • String comparison is probably not the best way to compare numbers. – Aadit M Shah Oct 31 '14 at 13:16
  • 1
    @AaditMShah Of course it's not. But in this one (insane) case, where we need to treat `-0` and `+0` as different entities, it's not really that much weirder than relying on different versions of division-by-zero. – Paul Roub Oct 31 '14 at 13:26
1

Try the following:

var objects = [ 
    { first_nom: "dave",  value: "5"  },
    { first_nom: "roger", value: "-0" },
    { first_nom: "pete",  value: "+0" },
    { first_nom: "pete",  value: "-5" },
];

objects.sort(function (a, b) {
    var x = parseInt(a.value, 10);
    var y = parseInt(b.value, 10);

    if (x === 0 && y === 0)
        return 1 / x - 1 / y || 0;
    else return x - y;
});

alert(JSON.stringify(objects, null, 4));

The reason this works is because 1 / 0 is Infinity whereas 1 / -0 is -Infinity.

For example:

  1. First case: x = 0, y = 0:

    1 / x = Infinity
    1 / y = Infinity
    
    Infinity - Infinity = NaN
    
    NaN || 0 = 0
    

    Hence the two numbers are left unsorted with respect to each other.

  2. Second case: x = -0, y = -0:

    1 / x = -Infinity
    1 / y = -Infinity
    
    (-Infinity) - (-Infinity) = NaN
    
    NaN || 0 = 0
    

    Hence the two numbers are left unsorted with respect to each other.

  3. Third case: x = 0, y = -0:

    1 / x = Infinity
    1 / y = -Infinity
    
    Infinity - (-Infinity) = Infinity
    
    Infinity || 0 = Infinity
    

    Hence x will come after y.

  4. Third case: x = -0, y = 0:

    1 / x = -Infinity
    1 / y = Infinity
    
    (-Infinity) - Infinity = -Infinity
    
    -Infinity || 0 = -Infinity
    

    Hence x will come before y.

That's all that there is to it really.


Edit: To sort it in reverse order just swap x and y as follows:

var objects = [ 
    { first_nom: "dave",  value: "5"  },
    { first_nom: "roger", value: "-0" },
    { first_nom: "pete",  value: "+0" },
    { first_nom: "pete",  value: "-5" },
];

objects.sort(function (a, b) {
    var x = parseInt(a.value, 10);
    var y = parseInt(b.value, 10);

    if (x === 0 && y === 0)
        return 1 / y - 1 / x || 0;
    else return y - x;
});

alert(JSON.stringify(objects, null, 4));

Hope that helps.

Aadit M Shah
  • 72,912
  • 30
  • 168
  • 299
  • @Nevnev You should accept the best answer to your question. Click on the tick mark next to the best answer. – Aadit M Shah Oct 31 '14 at 13:10
  • You might want to edit your sample code to sort in the (reversed) order that the Question asks for. – Paul Roub Oct 31 '14 at 13:27
  • If I wanted to add a secondary sort on first_nom how can I work that into your code above? @AaditMShah – Nevnev Dec 03 '14 at 10:46
  • PS this will be the last time I bother you :) @AaditMShah – Nevnev Dec 03 '14 at 10:59
  • @Nevnev Just change `return 1 / y - 1 / x || 0` to `return 1 / y - 1 / x || b.first_nom.localeCompare(a.first_nom)`; and change `return y - x` to `return y - x || b.first_nom.localeCompare(a.first_nom)`. – Aadit M Shah Dec 03 '14 at 11:07
  • Brilliant. thank you. Sorry about bothering you with such trivial tasks. @AaditMShah – Nevnev Dec 03 '14 at 11:20
0

A simple solution is to convert a signed zero to some signed small value, for example:

var objects = [ 
    { first_nom: 'dave', value: '5'},
    { first_nom: 'pete', value: '+0' },
    { first_nom: 'pete', value: '-5' },
    { first_nom: 'roger', value: '-0'},
];

  
convert = function(n) {
  if(n == '-0') return -Number.EPSILON;
  if(n == '+0') return +Number.EPSILON;  
  return parseInt(n);
}
  
objects.sort(function(a, b) {
  return convert(a.value) - convert(b.value);
});
  

document.write(JSON.stringify(objects));

To do that in a pure numeric way, utilize the fact that

Math.atan2(0, -0) === Math.PI 
Math.atan2(0,  0) === 0       

(see Differentiating +0 and -0)

var objects = [ 
    { first_nom: 'dave', value: '5'},
    { first_nom: 'roger', value: '-0'},
    { first_nom: 'pete', value: '+0' },
    { first_nom: 'pete', value: '-5' },
];

objects.sort(function(a, b) {
  a = parseInt(a.value);
  b = parseInt(b.value);
  return (a || b) ? (b - a) : (Math.atan2(0, a) - Math.atan2(0, b));
});
  
document.write(JSON.stringify(objects));
Community
  • 1
  • 1
georg
  • 211,518
  • 52
  • 313
  • 390