Javascript: натуральный вид буквенно-цифровых строк

Я ищу самый простой способ сортировки массива, состоящего из чисел и текста, и их комбинации.

например.

'123asd'
'19asd'
'12345asd'
'asd123'
'asd12'

превращается в

'19asd'
'123asd'
'12345asd'
'asd12'
'asd123'

Это будет использоваться в сочетании с решением другого вопроса, который я задал здесь.

Функция сортировки сама по себе работает, мне нужна функция, которая может сказать, что "19asd" меньше, чем "123asd".

Я пишу это в JavaScript.

Изменить: как отметил adormitu, я ищу функцию естественной сортировки

Ответ 1

Теперь это возможно в современных браузерах с использованием localeCompare. Передавая numeric: true вариант, он будет разумно распознавать числа. Вы можете нечувствительно к регистру использовать sensitivity: 'base'. Протестировано в Chrome, Firefox и IE11.

Вот пример. Он возвращает 1, то есть 10 идет после 2:

'10'.localeCompare('2', undefined, {numeric: true, sensitivity: 'base'})

Для производительности при сортировке большого количества строк в статье говорится:

При сравнении большого количества строк, например при сортировке больших массивов, лучше создать объект Intl.Collator и использовать функцию, предоставляемую свойством compare. Ссылка Docs

var collator = new Intl.Collator(undefined, {numeric: true, sensitivity: 'base'});
var myArray = ['1_Document', '11_Document', '2_Document'];
console.log(myArray.sort(collator.compare));

Ответ 2

Итак, вам нужен натуральный вид?

Если это так, то этот script Брайан Хуисман, основанный на работе Дэвида Коуэла, будет тем, что вам нужно.

Кажется, что решение Брайана Хуисмана теперь находится прямо на блоге Дэвида Коэлла:

Ответ 3

Для сравнения значений вы можете использовать метод сравнения -

function naturalSorter(as, bs){
    var a, b, a1, b1, i= 0, n, L,
    rx=/(\.\d+)|(\d+(\.\d+)?)|([^\d.]+)|(\.\D+)|(\.$)/g;
    if(as=== bs) return 0;
    a= as.toLowerCase().match(rx);
    b= bs.toLowerCase().match(rx);
    L= a.length;
    while(i<L){
        if(!b[i]) return 1;
        a1= a[i],
        b1= b[i++];
        if(a1!== b1){
            n= a1-b1;
            if(!isNaN(n)) return n;
            return a1>b1? 1:-1;
        }
    }
    return b[i]? -1:0;
}

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

function naturalSort(ar, index){
    var L= ar.length, i, who, next, 
    isi= typeof index== 'number', 
    rx=  /(\.\d+)|(\d+(\.\d+)?)|([^\d.]+)|(\.(\D+|$))/g;
    function nSort(aa, bb){
        var a= aa[0], b= bb[0], a1, b1, i= 0, n, L= a.length;
        while(i<L){
            if(!b[i]) return 1;
            a1= a[i];
            b1= b[i++];
            if(a1!== b1){
                n= a1-b1;
                if(!isNaN(n)) return n;
                return a1>b1? 1: -1;
            }
        }
        return b[i]!= undefined? -1: 0;
    }
    for(i= 0; i<L; i++){
        who= ar[i];
        next= isi? ar[i][index] || '': who;
        ar[i]= [String(next).toLowerCase().match(rx), who];
    }
    ar.sort(nSort);
    for(i= 0; i<L; i++){
        ar[i]= ar[i][1];
    }
}

Ответ 4

Представьте себе 8-значную функцию заполнения, которая преобразует:

  • '123asd' → '00000123asd'
  • '19asd' → '00000019asd'

Мы можем использовать проложенные строки, чтобы помочь нам сортировать "19asd", чтобы появиться перед "123asd".

Используйте регулярное выражение /\d+/g чтобы найти все номера, которые необходимо добавить:

str.replace(/\d+/g, pad)

Следующее демонстрирует сортировку с использованием этого метода:

var list = [
    '123asd',
    '19asd',
    '12345asd',
    'asd123',
    'asd12'
];

function pad(n) { return ("00000000" + n).substr(-8); }
function natural_expand(a) { return a.replace(/\d+/g, pad) };
function natural_compare(a, b) {
    return natural_expand(a).localeCompare(natural_expand(b));
}

console.log(list.map(natural_expand).sort()); // intermediate values
console.log(list.sort(natural_compare)); // result

Ответ 5

Основываясь на @Adrien Будьте выше и используйте код, который Брайан Хюисман и David koelle, вот модифицированная сортировка прототипов для массива объектов:

//Usage: unsortedArrayOfObjects.alphaNumObjectSort("name");
//Test Case: var unsortedArrayOfObjects = [{name: "a1"}, {name: "a2"}, {name: "a3"}, {name: "a10"}, {name: "a5"}, {name: "a13"}, {name: "a20"}, {name: "a8"}, {name: "8b7uaf5q11"}];
//Sorted: [{name: "8b7uaf5q11"}, {name: "a1"}, {name: "a2"}, {name: "a3"}, {name: "a5"}, {name: "a8"}, {name: "a10"}, {name: "a13"}, {name: "a20"}]

// **Sorts in place**
Array.prototype.alphaNumObjectSort = function(attribute, caseInsensitive) {
  for (var z = 0, t; t = this[z]; z++) {
    this[z].sortArray = new Array();
    var x = 0, y = -1, n = 0, i, j;

    while (i = (j = t[attribute].charAt(x++)).charCodeAt(0)) {
      var m = (i == 46 || (i >=48 && i <= 57));
      if (m !== n) {
        this[z].sortArray[++y] = "";
        n = m;
      }
      this[z].sortArray[y] += j;
    }
  }

  this.sort(function(a, b) {
    for (var x = 0, aa, bb; (aa = a.sortArray[x]) && (bb = b.sortArray[x]); x++) {
      if (caseInsensitive) {
        aa = aa.toLowerCase();
        bb = bb.toLowerCase();
      }
      if (aa !== bb) {
        var c = Number(aa), d = Number(bb);
        if (c == aa && d == bb) {
          return c - d;
        } else {
          return (aa > bb) ? 1 : -1;
        }
      }
    }

    return a.sortArray.length - b.sortArray.length;
  });

  for (var z = 0; z < this.length; z++) {
    // Here we're deleting the unused "sortArray" instead of joining the string parts
    delete this[z]["sortArray"];
  }
}