4

I currently have a set of strings that are both just numbers and number with + or -. Such as follows :

1 , 1+, 1-, 2, 2+, 2-, 10

Which when I sort using JavaScript's sort functions gives out:

1, 1+ , 1-, 10, 2, 2+, 2-

which is lexicographically orders but not numerically. Is there a way to sort this so the numbers come out in the correct way(the first list) ? I am using ExtJS stores so an answers as a store sorter is preferred but plain javascript is also fine. Thanks ?

Edit: This is not just sorting numbers.

oneCoderToRuleThemAll
  • 834
  • 2
  • 12
  • 33
  • 1
    `1+`, `1-` etc. are not numbers, how to order numerically something which are not numbers? – Teemu Oct 14 '16 at 18:13
  • is `1-` negative? – Nina Scholz Oct 14 '16 at 18:13
  • 1
    @NinaScholz No. 1 and the symbol- – oneCoderToRuleThemAll Oct 14 '16 at 18:13
  • Possible duplicate of [How to sort an array of integers correctly](http://stackoverflow.com/questions/1063007/how-to-sort-an-array-of-integers-correctly) – Quangdao Nguyen Oct 14 '16 at 18:14
  • 2
    *This is not just sorting numbers.* what else? – Nina Scholz Oct 14 '16 at 18:20
  • @GdgamesGamers What is expected result? – guest271314 Oct 14 '16 at 18:27
  • @guest271314 "_the correct way (the first list)_" – Teemu Oct 14 '16 at 18:29
  • @Teemu Still not following. If the list is already _"Such as follows : 1 , 1+, 1-, 2, 2+, 2-, 10"_, and that is _"the correct way(the first list)"_, why is there a need to "sort"? – guest271314 Oct 14 '16 at 18:40
  • @guest271314 I guess the first presentation is just a shortcut to show the correct order and an example together. Probably a real array to be sorted can be in any order. – Teemu Oct 14 '16 at 18:42
  • @Teemu That is not what the text of the Question presents: _"I currently have a set of strings that are both just numbers and number with + or -. Such as follows : 1 , 1+, 1-, 2, 2+, 2-, 10"_ Your solution places `"2-"` after `"2+"` and before `"10"`. Is the sort based on numeric order? If yes, how can `-2` be after `2`? – guest271314 Oct 14 '16 at 18:44
  • 1
    @guest271314 That's the order in the OP's first example. Also that is the order in my answer as well. It's a "number order", but OP has defined the order for symbols too. Which is (if I haven't misunderstood the question?) `number, number+, number-`. When OP says, that the first list is in correct order, shouldn't a sort function return this same order despite of the original array being in a correct order already? – Teemu Oct 14 '16 at 18:52

6 Answers6

3

You can use a custom ordering function like so:

var numbers = ['1', '1-', '1+', '2', '2+', '2-', '10'];

numbers.sort(function (a, b){
    var _a = parseFloat(a), // If the values are integers only, parseInt will do too
        _b = parseFloat(b);
    if (_a - _b === 0) {
     return (a > b) ? 1 : -1;
    } else {
     return _a - _b;
    }
});

console.log(numbers);

The function checks whether the number values are equal, and if so, falls back to lexicographic ordering to sort the character suffixes. If there are no suffixes in equal-case, hence no matter in which order the numbers are returned. If only one of the operands has a suffix, bare number returns negative. If the number values are not equal, the function simply returns the tristate, i.e a - b, which will be evaluated to one of negative, 0, positive. Or actually it's "bistate", since we've handled 0 case already.


More generic solution

The code above is rather a special case for two different single charactered suffixes only. If suffixes are more complex, here's a more generic code to sort by number and by suffixes:

var numbers = ['1', '1-r', '1+q', '1', '2', '2+q', '2-r', '10'];
function suffixSort (suff, asc) {
    asc = 2 * +(!!asc) - 1; // Convert boolean to -1 or 1
    return function (a, b) {
        var _a = parseFloat(a), // Extract the number value
            _b = parseFloat(b),
            aSI = -(a.length - _a.toString().length), // Get the index of suffix start
            bSI = -(b.length - _b.toString().length);
        // Equal number values, sort by suffixes
        if (_a === _b) {
            return (suff.indexOf(a.substr(aSI)) > suff.indexOf(b.substr(bSI))) ? 1 : -1;
        }
        // Inequal number values, sort by numbers
        return asc * (_a - _b);
    }
}
// suffixSort arguments
//   suff: An array of the suffix strings to sort, ordered in the desired sorting order
//   asc:  true = ascending, false = descending. Optional, defaults to descending sort
numbers.sort(suffixSort(['+q', '-r'], true));
console.log(numbers);

The idea is to store the suffixes into an array, and when suffix sorting is needed, function compares the array indices of the suffixes instead of the suffixes themselves.

suffixSort lets you also to decide the sorting direction. Selected sorting direction doesn't have an effect on suffix sorting, they are always returned in the order they appear in suff array.

Teemu
  • 22,918
  • 7
  • 53
  • 106
0

You could use Array#sort and split the elements in numbers and the rest, then return the difference or the difference of the order.

var array = ['10', '2', '2+', '2-', '1', '1+', '1-'];

array.sort(function (a, b) {
    var r = /\d+|\D+/g,
        aa = a.match(r),
        bb = b.match(r),
        order = { '+': 1, '-': 2 };

    return aa[0] - bb[0] || (order[aa[1]] || 0) - (order[bb[1]] || 0);
});

console.log(array);
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
0
  var result=[]; 
        result=array.map(function(n){
          if(typeof n==='number') return n;
            if(n[n.length-1]=='+'){
              return parseInt(n.substring(0,n.length-1))
              }
            else if(n[n.length-1]=='-'){
               return 0-parseInt(n.substring(0,n.length-1))
               }
            });
 result.sort(function(a,b){return a-b})
nivendha
  • 792
  • 7
  • 16
0

These values are almost integers, so comparing them according to praseInt will almost get you there. The only thing missing is a special treatment for values that have the same integer part where x- should come first, then x and finally x+:

function specialChar(s) {
    c = s.substr(-1);
    if (c == '+') {
        return 1;
    }
    if (c == '-') {
      return -1;
    }
    return 0;
}

function numCompare(a, b) {
    aNum = parseInt(a);
    bNum = parseInt(b);
    cmp = aNum - bNum;
    if (cmp != 0) {
        return cmp;
    }
    // Integer parts are equal - compare the special char at the end
    return specialChar(a) - specialChar(b);
}

arr = ['1' , '1+', '1-', '2', '2+', '2-', '10'];
arr.sort(numCompare);
Mureinik
  • 297,002
  • 52
  • 306
  • 350
  • `if (cmp != 0) { return cmp; }` Wouldn't this incorrectly handle '3-' and '2+'? – serg Oct 14 '16 at 18:34
  • @serg Why do you think so? As far as I understand the OP's requirements, `2+` (read: two plus) should come before `3-` (read: three minus, **not** negative three). – Mureinik Oct 14 '16 at 18:38
  • Ah if its three minus then it's correct, I thought it's minus three for some reason. – serg Oct 14 '16 at 18:43
0

If there are only three possible states of a number, and the states have the order number, number+, number the states can be recreated by creating an array representation of the numbers, removing the unique numbers from array, from minimum to maximum, concatenating empty string or arithmetic operator in required order to the number, then pushing the value to an array, where .toString() can be used to view the comma separated string representation of the sorted values within the array

var str = `314+, 1-, 7+, 1, 1-, 271-, 10-
          , 10+, 271, 271+, 314-, 314
          , 10, 2-, 2, 2+, 7-, 7`;

for (var [nums, order, res, num] = [str.match(/\d+/g), ["", "+", "-"], [], null]
     ; nums.length
     ; num = Math.min.apply(Math, nums)
       , res = [...res, ...order.map(op => num + op)]
       , nums = nums.filter(n => n != num)
    );

console.log(res.toString() + "\n", res);
guest271314
  • 1
  • 15
  • 104
  • 177
-1

Assuming that you just want to throw away the symbols, then you could use parseInt and Array#sort to get order numerically.

var data = ['1' , '1+', '1-', '2', '2+', '2-', '10'];
var sortedData = data.sort(function(a,b){return parseInt(a)-parseInt(b);});