简体   繁体   中英

How to sort set of numbers both lexicographically and numerically?

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.

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, ie 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.

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); 

  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})

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);

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); 

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);});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM