如何比较字符串和数值(考虑负数,并使空值始终排在最后)?

9
我正在尝试对一个由数字或字符串值混合而成的值数组进行排序(例如[10,"20",null,"1","bar","-2",-3,null,5,"foo"])。如何排序此数组,以使
  • null 值总是放在最后(无论排序顺序如何,详见 jsFiddle)
  • 负数被正确排序(即它们小于正数,并在自身之间正确排序)
我使用jsFiddle制作了详细的数字和字符串示例(使用localeComparenumeric选项),但以下是我的排序算法的数字版本,以供参考。
// Sorting order
var order = "asc"; // Try switching between "asc" and "dsc"

// Dummy arrays
var numericArr = [10,20,null,1,-2,-3,null,5];

// Sort arrays
$(".output1").append(numericArr.toString());
numericArr.sort(sortByDataNumeric);
$(".output2").append(numericArr.toString());

// Numeric sorting function
function sortByDataNumeric(a, b, _order) {

    // Replace internal parameters if not used
    if (_order == null) _order = order;

    // If values are null, place them at the end
    var dflt = (_order == "asc" ? Number.MAX_VALUE : -Number.MAX_VALUE);

    // Numeric values
    var aVal = (a == null ? dflt : a);
    var bVal = (b == null ? dflt : b);
    return _order == "asc" ? (aVal - bVal) : (bVal - aVal);
}

我的字符串排序算法(参见jsFiddle)存在问题,我无法找到一种方法始终将null值放置在最后,并且负值在自身内部未正确排序(例如,-3应该小于-2)。

编辑

回答评论,我希望[10,"20",null,"1","bar","-2",-3,null,5,"foo"]排序为[-3,"-2","1",5,10,"20","bar","foo",null,null]

1
"bar" 应该怎么处理?它既不是数字,也不是数值字符串,也不是 null - Bergi
1
"20"(字符串)应该排在整数20之前还是之后?或者排在所有数字值之后? - andi
1
你想如何对字符串进行排序?相关示例:["-1", 3, "10", "foo"] - Tibos
@Bergi 已添加了预期的答案,谢谢。 - lebolo
很好的问题@andi,我实际上更喜欢它们被视为相等进行比较... - lebolo
显示剩余6条评论
4个回答

11

首先要检查是否有任何一个值为 null ,如果是则返回另一个值。


顺便提一下:

对于您的默认 _order 值,应该检查参数是否为 undefined ,而不是将其值与 null 进行比较。如果直接比较未定义的内容,将会得到引用错误:

(undefinedVar == null) // ReferenceError: undefinedVar is not defined

相反,你应该检查变量是否未定义:

(typeof undefinedVar == "undefined") // true

另外,将比较函数封装在闭包中,而不是依赖于全局的 order 变量,可能是一个更好的主意。

例如:

[].sort(function(a, b){ return sort(a, b, order)})

这样,您可以在每个实例级别上进行排序。


http://jsfiddle.net/gxFGN/10/

JavaScript(JavaScript)

function sort(a, b, asc) {
    var result;

    /* Default ascending order */
    if (typeof asc == "undefined") asc = true;

    if (a === null) return 1;
    if (b === null) return -1;
    if (a === null && b === null) return 0;

    result = a - b;

    if (isNaN(result)) {
        return (asc) ? a.toString().localeCompare(b) : b.toString().localeCompare(a);
    }
    else {
        return (asc) ? result : -result;
    }
}

2
这个不行。如果两个操作数都是null,你没有返回0,更糟糕的是,当比较10"bar"时,它会抛出异常a.localeCompare is not a function - 这个小工具已经坏了(在Opera排序算法中发生)。 - Bergi
1
我接受了这个答案,我认为它是最简洁和最干净的。然而,我修改了一下代码,使其更具体地检查 null:if (a === null && b !== null) return 1; if (a !== null && b === null) return -1; var result = a - b;。这使得它在规范方面更加一致(从实际角度来看几乎没有用处)。我还在调用 localCompare 函数之前强制执行了 toStringa.toString().localeCompare(b) - lebolo
谢谢 @Bergi。我已根据您的反馈更新了问题。我错误地认为 result === NaN 只有当两个字符串不能被强制转换为数值时才会发生。 - thgaskell

2
function sortByDataString(a, b) {
    if (a === null) {
        return 1;
    }
    if (b === null) {
        return -1;
    }
    if (isNumber(a) && isNumber(b)) {
        if (parseInt(a,10) === parseInt(b,10)) {
            return 0;
        }
        return parseInt(a,10) > parseInt(b,10) ? 1 : -1;
    }
    if (isNumber(a)) {
        return -1;
    }
    if (isNumber(b)) {
        return 1;
    }
    if (a === b) {
        return 0;
    }
    return a > b ? 1 : -1;
}

这里有一个fiddle示例:http://jsfiddle.net/gxFGN/6/

我忽略了顺序参数,但是如果需要,您可以在最后反转数组。


运行你的 jsFiddle,我得到了 -2,1,10,20,bar,-3,5,foo,null,null 的结果,但我想要 -3,-2,1,5,10,20,bar,foo,null,null - lebolo
哇,我刚刚尝试了三个不同的浏览器,得到了三个不同的结果。看来这并不简单啊。=) - andi
即使项目相等,您也永远不会返回0。排序算法需要一个适当的比较函数,否则它们就会变得不可预测(来自3个不同的排序实现,例如浏览器),导致三种不同的不可预测结果。 - Bergi
@Bergi - 你说得对,如果字符串相等的话最好返回0,但这并不是浏览器之间差异巨大的原因 - 不管怎样我都遇到了同样的问题。不过我会在这个可用的函数中加入这个功能。 - andi
@Bergi - 类似于您的代码,但在 if (isNumber(a) && isNumber(b)) 情况下还添加了返回 0 的语句。 - andi
显示剩余3条评论

1
使用这个:
function typeOrder(x) {
    if (x == null)
        return 2;
    if (isNaN(+x))
        return 1;
    return 0;
}
function sortNumber(a, b) {
    a = parseInt(a, 10); b = parseInt(b, 10);
    if (isNaN(a) || isNaN(b))
        return 0;
    return a - b;
}
function sortString(a, b) {
    if (typeof a != "string" || typeof b != "string")
       return 0;
    return +(a > b) || -(b > a);
}

order = order == "dsc" ? -1 : 1;
numericArr.sort(function(a, b) {
    return order * ( typeOrder(a)-typeOrder(b)
                     || sortNumber(a, b)
                     || sortString(a, b)
                   );
});

(更新的 fiddle)


0

我非常确定你的问题是一个误导... 你传递给 sort 的抽象函数没有得到第三个参数(在你的情况下是 _order)。所以在你的情况下,它总是会是 undefined

请考虑这一点重新审视你的代码并查看结果。

你指定的数组完全是数字,所以你的排序应该能正常工作,尽管其他评论者建议,如果你的数组最终包含字符串值(例如 "10"、"-7" 等),你需要在进行比较之前使用 parseInt 并测试 isNaN。


我知道,这就是为什么我会写 if (_order == null) _order = order,因为 order 在作用域内。尝试在 jsFiddle 中更改 order 变量的值,从 'asc'/'dsc' 之间切换,你会发现它确实有效。jsFiddle 还提供了字符串数组示例。这是数字、字符串和 null 的组合变得困难的地方。 - lebolo
1
但它不是 null,而是 undefined,这两者并不相同。 - Yevgeny Simkin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接