как сортировать строки в JavaScript численно

Я хотел бы отсортировать массив строк (в javascript) так, чтобы группы цифр внутри строк сравнивались как целые числа, а не строки. Меня не волнуют числа со знаком или с плавающей запятой.

например, результат должен быть ["a1b3","a9b2","a10b2","a10b11"] не ["a1b3","a10b11","a10b2","a9b2"]

самый простой способ сделать это-разбить каждую строку на границы вокруг групп цифр. Есть ли шаблон, который я могу передать String.split для разделения по границам символов без удаления любые символы?

"abc11def22ghi".split(/?/) = ["abc","11","def","22","ghi"];

или есть другой способ сравнения строк, который не включает их разделение, возможно, путем заполнения всех групп цифр ведущими нулями, чтобы они были одинаковой длины?

"aa1bb" => "aa00000001bb", "aa10bb" => "aa00000010bb"

Я работаю с произвольными строками, а не строками, которые имеют определенное расположение цифровых групп.

Edit:

мне нравится /(d+)/ один лайнер от Gaby, чтобы разделить массив. Как обратная совместимость это так?

решения, которые анализируют строки один раз таким образом, что их можно использовать для перестройки оригиналов, намного эффективнее этой функции сравнения. Ни один из ответов не обрабатывает некоторые строки, начинающиеся с цифр, а другие нет, но это было бы достаточно легко исправить и не было явным в исходном вопросе.

["a100","a20","a3","a3b","a3b100","a3b20","a3b3","!!","~~","9","10","9.5"].sort( function ( inA , inB ) {
    var                     result = 0;

    var                     a , b , pattern = /(d+)/;
    var                     as = inA.split( pattern );
    var                     bs = inB.split( pattern );
    var                     index , count = as.length;

    if ( ( '' === as[0] ) === ( '' === bs[0] ) ) {
        if ( count > bs.length ) count = bs.length;

        for ( index = 0 ; index < count && 0 === result ; ++index ) {
            a = as[index]; b = bs[index];

            if ( index & 1 ) {
                result = a - b;
            } else {
                result = !( a < b ) ? ( a > b ) ? 1 : 0 : -1;
            }
        }

        if ( 0 === result ) result = as.length - bs.length;
    } else {
        result = !( inA < inB ) ? ( inA > inB ) ? 1 : 0 : -1;
    }

    return result;
} ).toString();

результат: "!!,9,9.5,10,a3,a3b,a3b3,a3b20,a3b100,a20,a100,~~"

6 ответов


Я думаю, что это делает то, что вы хотите

function sortArray(arr) {
    var tempArr = [], n;
    for (var i in arr) {
        tempArr[i] = arr[i].match(/([^0-9]+)|([0-9]+)/g);
        for (var j in tempArr[i]) {
            if( ! isNaN(n = parseInt(tempArr[i][j])) ){
                tempArr[i][j] = n;
            }
        }
    }
    tempArr.sort(function (x, y) {
        for (var i in x) {
            if (y.length < i || x[i] < y[i]) {
                return -1; // x is longer
            }
            if (x[i] > y[i]) {
                return 1;
            }
        }
        return 0;
    });
    for (var i in tempArr) {
        arr[i] = tempArr[i].join('');
    }
    return arr;
}
alert(
    sortArray(["a1b3", "a10b11", "a10b2", "a9b2"]).join(",")
);

предполагая, что вы хотите сделать, это просто сделать числовую сортировку по цифрам в каждой записи массива (игнорируя не цифры), вы можете использовать это:

function sortByDigits(array) {
    var re = /\D/g;

    array.sort(function(a, b) {
        return(parseInt(a.replace(re, ""), 10) - parseInt(b.replace(re, ""), 10));
    });
    return(array);
}

Он использует пользовательскую функцию сортировки, которая удаляет цифры и преобразуется в число каждый раз, когда его просят сделать сравнение. Вы можете увидеть его работу здесь:http://jsfiddle.net/jfriend00/t87m2/.

Если это не то, что вы хотите, то, пожалуйста, уточните, как ваш вопрос не очень ясно на hwo сортировка должна фактически работать.


используйте эту функцию сравнения для сортировки ..

function compareLists(a,b){
    var alist = a.split(/(\d+)/), // split text on change from anything to digit and digit to anything
        blist = b.split(/(\d+)/); // split text on change from anything to digit and digit to anything

    alist.slice(-1) == '' ? alist.pop() : null; // remove the last element if empty
    blist.slice(-1) == '' ? blist.pop() : null; // remove the last element if empty

    for (var i = 0, len = alist.length; i < len;i++){
        if (alist[i] != blist[i]){ // find the first non-equal part
           if (alist[i].match(/\d/)) // if numeric
           {
              return +alist[i] - +blist[i]; // compare as number
           } else {
              return alist[i].localeCompare(blist[i]); // compare as string
           }
        }
    }

    return true;
}

синтаксис

var data = ["a1b3","a10b11","b10b2","a9b2","a1b20","a1c4"];
data.sort( compareLists );
alert(data);

демо на http://jsfiddle.net/h9Rqr/7/


вот более полное решение это сортируется в соответствии с буквами и цифрами в строках

function sort(list) {
    var i, l, mi, ml, x;
    // copy the original array
    list = list.slice(0);

    // split the strings, converting numeric (integer) parts to integers
    // and leaving letters as strings
    for( i = 0, l = list.length; i < l; i++ ) {
        list[i] = list[i].match(/(\d+|[a-z]+)/g);
        for( mi = 0, ml = list[i].length; mi < ml ; mi++ ) {
            x = parseInt(list[i][mi], 10);
            list[i][mi] = !!x || x === 0 ? x : list[i][mi];
        }
    }

    // sort deeply, without comparing integers as strings
    list = list.sort(function(a, b) {
        var i = 0, l = a.length, res = 0;
        while( res === 0 && i < l) {
            if( a[i] !== b[i] ) {
                res = a[i] < b[i] ? -1 : 1;
                break;
            }

            // If you want to ignore the letters, and only sort by numbers
            // use this instead:
            // 
            // if( typeof a[i] === "number" && a[i] !== b[i] ) {
            //     res = a[i] < b[i] ? -1 : 1;
            //     break;
            // }

            i++;
        }
        return res;
    });

    // glue it together again
    for( i = 0, l = list.length; i < l; i++ ) {
        list[i] = list[i].join("");
    }
    return list;
}

сортировка происходит слева направо, если вы создаете пользовательский алгоритм. Буквы или цифры сравниваются сначала цифрами, а затем буквами.

однако то, что вы хотите выполнить в соответствии с вашим собственным примером (a1, a9, a10), никогда не произойдет. Это потребует, чтобы вы знали данные перед рукой и разбивали строку всеми возможными способами перед применением сортировки.

последней альтернативой было бы:

a) разбейте каждую строку слева направо когда происходит переход от буквы к цифре и наоборот; & b) затем начните сортировку по этим группам справа налево. Это будет очень сложный алгоритм. Можно сделать!

наконец, если вы являетесь генератором исходного "текста", вы должны рассмотреть возможность нормализации вывода, где a1 a9 a10 может быть выведен как a01 a09 a10. Таким образом, вы можете иметь полный cotnrol окончательной версии алгоритма.

удачи!


Мне нужен был способ взять смешанную строку и создать строку, которая могла бы быть отсортирована в другом месте, чтобы числа сортировались численно и Буквы в алфавитном порядке. Основываясь на приведенных выше ответах, я создал следующее, которое заполняет все числа так, как я могу понять, где бы они ни появлялись в строке.

function padAllNumbers(strIn) {
    // Used to create mixed strings that sort numerically as well as non-numerically
    var patternDigits = /(\d+)/g; // This recognises digit/non-digit boundaries
    var astrIn = strIn.split( patternDigits ); // we create an array of alternating digit/non-digit groups

    var result = "";

    for (var i=0;i<astrIn.length;  i++) {
        if (astrIn[i] != "") { // first and last elements can be "" and we don't want these padded out
            if (isNaN(astrIn[i])) {
                result += astrIn[i];
            } else {
                result += padOneNumberString("000000000",astrIn[i]);
            }
        }
    }
    return result;
}

function padOneNumberString(pad,strNum,left) {
    // Pad out a string at left (or right)
    if (typeof strNum === "undefined") return pad;
    if (typeof left === "undefined") left = true;
    var padLen =  pad.length - (""+ strNum).length;
    var padding = pad.substr(0,padLen);
    return left?  padding + strNum : strNum + padding;
}