【问题标题】:Sorting "advanced" numeric strings by numeric order按数字顺序对“高级”数字字符串进行排序
【发布时间】:2017-03-14 07:54:27
【问题描述】:

想象一下有这个数组:

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" 是一个较低的值。所以基本上希望是按第一个数字升序排序,如果第一个数字在两个值之间相同,则对第二个数字进行排序

【问题讨论】:

  • 您是说要按每个总和的结果排序,还是按每个总和的第一个数字的值排序(对于显示的数据,这将给出相同的结果)?
  • @nnnnnn 该值应被视为字符串,因此根本没有数学运算。现在的问题是“$ 100 + $ 9”被视为低于“$ 2 + $ 0.5”的价值,这不是我想要实现的目标。
  • @nikkop:这没有回答 nnnnnn 的问题,也没有真正的意义。通过将它们视为字符串,您无法(合理地)获得所需的顺序。您要么 A)提取第一个操作数的数值并按其排序,要么 B)求和并按结果排序。你做什么取决于你想要什么最终结果。正如 nnnnnn 所说,两者都给出了上面的示例结果(但并非所有输入都会)。
  • 如果你真的想把字符串当成字符串,隐式地按字符串中的第一个数字排序,你想使用自然排序。这会使这个问题可能与javascript natural sort 重复
  • @T.J.Crowder 对不起,如果我误解了这个问题。但是,求和然后排序会产生不需要的结果,请考虑"$0.9 + $0.1""$0.7 + $0.4" 的示例。 "$0.9 + $0.1" 将是一个较低的值,因为总和是 1,但我想排序以便 "$0.7 + $0.4" 是一个较低的值。所以基本上希望是按第一个数字升序排序,如果第一个数字在两个值之间相同,则按第二个数字排序。

标签: javascript arrays sorting numeric


【解决方案1】:

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

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

【讨论】:

  • 感谢此代码。但是我不希望对总和进行排序,因为它会产生不需要的结果(请查看我更新信息的原始帖子)
  • 非常感谢,但不幸的是,您的需要代码输出了一个奇怪的顺序:["$0.9 + $0.1", "$20 + $2", "$0.7 + $0.4", "$5 + $0.5", "$0 + $0.01", "$100 + $9", "$1 + $1", "$2 + $0.5"]
【解决方案2】:

我会使用这个三步算法:

  1. 为每个元素添加要排序的两个值
  2. 按这些值排序
  3. 再次删除该额外信息

这是 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 将从三元组中获取原始字符串,从而删除用于排序的额外信息。

在下面的 sn-p 中,我向您的示例数据 ($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 回调时可以获得性能。

【讨论】:

  • 添加了一个不按总和而是按单个值排序的变体。
  • 像魅力一样工作!输出是所需的。非常感谢您的描述,我从中学到了很多。
【解决方案3】:

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-06-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多