按数字顺序排序“高级”数字字符串

时间:2017-03-14 07:54:27

标签: javascript arrays sorting numeric

想象一下这个数组:

var fees = [
    '$0.9 + $0.1',
    '$20 + $2',
    '$0.7 + $0.4',
    '$5 + $0.5',
    '$0 + $0.01',
    '$100 + $9',
    '$1 + $1',
    '$2 + $0.5'
];

我如何使用vanilla JavaScript,以数字升序排序这些字符串值?

排序后所需的输出:

['$0 + $0.01', '$0.7 + $0.4', '$0.9 + $0.1', '$1 + $1', '$2 + $0.5', '$5 + $0.5', '$20 + $2', '$100 + $9']

我尝试了以下内容:

function mySort(a, b) {
    return ((a < b) ? -1 : ((a > b) ? 1 : 0));
}

但这只是输出:

["$0 + $0.01", "$0.7 + $0.4", "$0.9 + $.1", "$1 + $1", "$100 + $9", "$2 + $0.5", "$20 + $2", "$5 + $0.5"]`

这是否可能以一种整洁和合乎逻辑的方式实现?

我不希望得到这笔钱,因为它会产生不必要的结果。考虑"$0.9 + $0.1""$0.7 + $0.4"的示例。 "$0.9 + $0.1"将是一个较低的值,因为总和为1,但我想排序,以便"$0.7 + $0.4"代替较低的值。所以基本上希望对第一个数字进行排序,如果第一个数字在两个值之间相同,那么对第二个数字进行排序

3 个答案:

答案 0 :(得分:5)

根据字符串中的数字总和进行排序。

var fees = ['$0.9 + $.1', '$20 + $2', '$5 + $0.5', '$0 + $0.01', '$100 + $9', '$1 + $1', '$2 + $0.5'];

fees.sort(function(a, b) {
  return getSum(a) - getSum(b);
})

function getSum(str) {
  return str
    // remove the $ and space
    .replace(/[^\d+.]/g, '')
    //split by + symbol
    .split('+')
    // get the sum
    .reduce(function(sum, s) {
      // parse and add with sum
      return sum + (Number(s) || 0);
      // set inital value as sum
    }, 0)
}

console.log(fees);

您可以使用另外一个保存总和的对象来加快处理速度。

var fees = ['$0.9 + $.1', '$20 + $2', '$5 + $0.5', '$0 + $0.01', '$100 + $9', '$1 + $1', '$2 + $0.5'];

var ref = fees.reduce(function(obj, str) {
  // define object property if not defined
  obj[str] = str in obj || str
    // remove the $ and space
    .replace(/[^\d+.]/g, '')
    //split by + symbol
    .split('+')
    // get the sum
    .reduce(function(sum, s) {
      // parse and add with sum
      return sum + (Number(s) || 0);
      // set inital value as sum
    }, 0);
  // return the object reference
  return obj;
  // set initial value as an empty objecct
}, {})

fees.sort(function(a, b) {
  return ref[a] - ref[b];
})



console.log(fees);

更新:由于您已更新了问题,因此需要对各个部分进行比较。

var fees = ['$0.9 + $.1', '$20 + $2', '$5 + $0.5', '$0 + $0.01', '$100 + $9', '$1 + $1', '$2 + $0.5'];

fees.sort(function(a, b) {
  // get numbers from a
  var arrA = a.replace(/[^\d.+]/g, '').split('+');
  // get numbers from b
  var arrB = b.replace(/[^\d.+]/g, '').split('+');

  // generate sort value
  return arrA[0] - arrB[0] || arrA[1] - arrB[1];
})

console.log(fees);

答案 1 :(得分:2)

您可以获取字符串的各个部分,添加它们并使用两个元素的增量进行排序。

&#13;
&#13;
var fees = ['$0.9 + $.1', '$20 + $2', '$5 + $0.5', '$0 + $0.01', '$100 + $9', '$1 + $1', '$2 + $0.5'];

fees.sort(function (a, b) {
    function getValues(s) {
        return s.match(/([0-9.]+)/g).map(Number);
    }
    
    var aa = getValues(a),
        bb = getValues(b);
    return aa[0] + aa[1] - (bb[0] + bb[1]);
});

console.log(fees);
&#13;
&#13;
&#13;

答案 2 :(得分:2)

我会使用这个三步算法:

  1. 向每个元素添加要按
  2. 排序的两个值
  3. 按这些值排序
  4. 再次删除这些额外信息
  5. 这是ES6代码。

    var result = fees.map( s => [s].concat(s.match(/[\d.]+/g).map(Number)) )
                    .sort( (a, b) => a[1] - b[1] || a[2] - b[2])
                    .map( a => a[0] );
    

    s.match()调用将产生一个包含两个匹配(字符串)的数组。 map()调用会将这些调用为数字,concat()会将这两个数字相加,以形成原始字符串的三元组。

    排序回调将按第一个数字(在索引1处)排序,如果相等(则差值为0),则第二个数字将用于排序。

    最终map将从三元组中获取原始字符串,从而删除用于排序的额外信息。

    在下面的代码片段中,我为您的示例数据添加了一个元素($5.1 + $0.1),这将显示与按顺序排序时所获得的输出之间的差异(您不需要,如问题的更新)。

    var fees = [
        '$0.9 + $.1',
        '$20 + $2',
        '$5 + $0.5',
        '$5.1 + $0.1', // smaller sum than previous, but larger first value
        '$0 + $0.01',
        '$100 + $9',
        '$1 + $1',
        '$2 + $0.5'
    ];
    
    // 1. add the sum to each of the elements
    // 2. sort by the sums
    // 3. drop the sums
    var result = fees.map( s => [s].concat(s.match(/[\d.]+/g).map(Number)) )
                    .sort( (a, b) => a[1] - b[1] || a[2] - b[2])
                    .map( a => a[0] );
    
    console.log(result);

    这种方式只会将正则表达式应用于每个字符串一次,这在sort回调中即时执行时并非(总是)。排序算法通常必须多次比较相同的值,因此在尽可能将sort回调移出逻辑时可以获得性能。