0

I have a list of data that I'm displaying in a graph, and I want the data to be displayed alphabetically. When the data points are all words (e.g. "white", "asian", "african american") my sorting function works correctly, but when my data points include numbers (e.g. "1", "10", "<1") the sorting doesn't work the way I need it to. This is my sorting script:

var series = response.series;

series = series.sort(function(a, b) {
    var textA = a.name.toLowerCase();
    var textB = b.name.toLowerCase();
    console.log("Comparing " + textA + " to " + textB);
    var returnVal = (textA < textB) ? -1 : (textA > textB) ? 1 : 0;
    console.log(returnVal);
    return returnVal;
});

I can see from the console logging that the comparisons appear to be comparing correctly, but when I check series again after the sorting, it's the same as before. This is a larger example of my data:

series = [
    {name: "1"}
    {name: "10"}
    {name: "11"}
    {name: "12"}
    {name: "13"}
    {name: "2"}
    {name: "3"}
    {name: "4"}
    {name: "5"}
    {name: "6"}
    {name: "7"}
    {name: "8"}
    {name: "9"}
    {name: "<1"}
    {name: "Total 18 and below"}
]
Erica Stockwell-Alpert
  • 4,624
  • 10
  • 63
  • 130
  • well you are sorting strings, not numbers – epascarello Apr 04 '18 at 18:27
  • 1
    Possible duplicate of [Alphanumeric sorting an array in JavaScript](https://stackoverflow.com/questions/19247495/alphanumeric-sorting-an-array-in-javascript) – Taplar Apr 04 '18 at 18:29
  • Possible duplicate sorts array of things, rather than an array of objects, but it sounds like you are asking for an alphanumeric sort implementation. – Taplar Apr 04 '18 at 18:31

2 Answers2

1

I realized that the values were being compared as strings rather than numbers, and in Javascript, "123" comes before "2", the same way "abc" comes before "b". I could not just convert my entire array to numbers since it included "<1" and "Total 18 and below", so I wrote a custom comparison method:

series = series.sort(function(a, b) {
    var textA = a.name.toLowerCase();
    var textB = b.name.toLowerCase();

    // if we're comparing number to number, do number sorting
    if (!isNaN(textA) && !isNaN(textB)) {
        var numA = parseInt(textA);
        var numB = parseInt(textB);
        return (numA < numB) ? -1 : (numA > numB) ? 1 : 0;
    }

    // else sort as strings
    return (textA < textB) ? -1 : (textA > textB) ? 1 : 0;
});
Erica Stockwell-Alpert
  • 4,624
  • 10
  • 63
  • 130
  • I'm not so sure it would produce an expected result, or otherwise I have misunderstood the question. For example, how would you like the strings `<2, 3, >4` to be sorted, or this is the cases which you don't care about? – nicael Apr 04 '18 at 18:33
  • have you run your code?(it not giving correct output) – yajiv Apr 04 '18 at 18:35
  • <1 comes at the end but that's fine, I just needed the numbers to be in numerical order – Erica Stockwell-Alpert Apr 05 '18 at 18:15
1

You could sort by numbers and if a comparison sign is available, then take for the same numerical value the delta of both offsets, which reflects the order of comparison.

For getting the right order of the test 'and below', I suggest to replace it with '<=' for the right order.

var array = [{ name: "1" }, { name: "10" }, { name: "11" }, { name: "12" }, { name: "13" }, { name: "2" }, { name: "3" }, { name: "4" }, { name: "5" }, { name: "6" }, { name: "7" }, { name: "8" }, { name: "9" }, { name: "<1" }, { name: "Total 18 and below" }, { name: "> 18" }, { name: "18" }];

array.sort(function (a, b) {
    function getV(s) {
        s = s.replace(/(\D*)(\d+)\s*(and below)/i, '<= $2');
        return {
            value: s.match(/\d+/)[0],
            offset: { '<': -2, '<=': -1, null: 0, '>=': 1, '>': 2 }[s.match(/[<>]={0,1}(?=\s*\d)/)]
        };
    }
    var aa = getV(a.name),
        bb = getV(b.name);
    return aa.value - bb.value || aa.offset - bb.offset;
});

console.log(array);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392