Javascript: naturalny rodzaj ciągów alfanumerycznych

Szukam najprostszego sposobu sortowania tablicy, która składa się z liczb i tekstu oraz ich kombinacji.

Np.

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

Zamienia się w

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

To będzie używane w połączeniu z rozwiązaniem kolejne pytanie zadałem tutaj .

Funkcja sortowania sama w sobie działa, potrzebuję funkcji, która może powiedzieć, że '19asd' jest mniejszy niż '123asd'.

Piszę to w JavaScript.

Edit: as adormitu ]}

Author: Muhammad Umer, 2010-05-10

7 answers

Jest to teraz możliwe w nowoczesnych przeglądarkach przy użyciu localeCompare. Przechodząc do opcji numeric: true, inteligentnie rozpozna liczby. Rozróżnienie wielkości liter można wykonać za pomocą sensitivity: 'base'. Testowane w Chrome, Firefox i IE11.

Oto przykład. Zwraca 1, czyli 10 idzie po 2:

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

Dla wydajności podczas sortowania dużej liczby ciągów, artykuł mówi:

Przy porównywaniu dużej liczby łańcuchów, np. przy sortowaniu dużych tablic, lepiej jest Utwórz Intl.Obiekt Collator i użyj funkcji dostarczonej przez jego właściwość compare. Docs link

var collator = new Intl.Collator(undefined, {numeric: true, sensitivity: 'base'});
var myArray = ['1_Document', '11_Document', '2_Document'];
console.log(myArray.sort(collator.compare));
 364
Author: frodo2975,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-15 10:04:20

Więc potrzebujesz naturalnego rodzaju ?

Jeśli tak, to może scenariusz Briana Huismana oparty na pracy Davida koelle byłby tym, czego potrzebujesz.

Wygląda na to, że rozwiązanie Briana Huismana jest teraz bezpośrednio hostowane na blogu Davida Koelle ' a:

 46
Author: mhitza,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-26 15:02:26

Aby porównać wartości można użyć metody porównawczej -

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

Ale dla szybkości sortowania tablicy, Ustaw tablicę przed sortowaniem, więc musisz tylko wykonać konwersje małych liter i wyrażenie regularne raz zamiast na każdym kroku.

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];
    }
}
 23
Author: kennebec,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-02-05 15:13:14

Jeśli masz tablicę obiektów możesz zrobić tak:

myArrayObjects = myArrayObjects.sort(function(a, b) {
  return a.name.localeCompare(b.name, undefined, {
    numeric: true,
    sensitivity: 'base'
  });
});

var myArrayObjects = [{
    "id": 1,
    "name": "1 example"
  },
  {
    "id": 2,
    "name": "100 example"
  },
  {
    "id": 3,
    "name": "12 example"
  },
  {
    "id": 4,
    "name": "5 example"
  },

]

myArrayObjects = myArrayObjects.sort(function(a, b) {
  return a.name.localeCompare(b.name, undefined, {
    numeric: true,
    sensitivity: 'base'
  });
});
console.log(myArrayObjects);
 22
Author: D0rm1nd0,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-03-31 23:27:03

[1]}najbardziej funkcjonalna biblioteka do obsługi tego od 2019 roku wydaje się być natural-orderby.

const { orderBy } = require('natural-orderby')

const unordered = [
  '123asd',
  '19asd',
  '12345asd',
  'asd123',
  'asd12'
]

const ordered = orderBy(unordered)

// [ '19asd',
//   '123asd',
//   '12345asd',
//   'asd12',
//   'asd123' ]

Nie tylko pobiera tablice łańcuchów, ale także może sortować według wartości określonego klucza w tablicy obiektów. Może również automatycznie identyfikować i sortować ciągi: waluty, daty, waluty i wiele innych rzeczy.

Zaskakująco, to tylko 1.6 kB, gdy gziped.

 5
Author: Julien,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-02-21 14:51:17

Wyobraź sobie 8-cyfrową funkcję padding, która przekształca:

  • '123asd' - > '00000123asd'
  • '19asd' - > '00000019asd'

Możemy użyć wyściełanych łańcuchów, aby pomóc nam sortować '19asd', aby pojawić się przed '123asd'.

Użyj wyrażenia regularnego /\d+/g, aby znaleźć wszystkie liczby, które muszą być wyściełane:

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

Poniżej przedstawiono sortowanie przy użyciu tej techniki:

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

Wyniki pośrednie pokazują, co funkcja natural_expand () pozwala zrozumieć, jak będzie działać kolejna funkcja natural_compare:

[
  "00000019asd",
  "00000123asd",
  "00012345asd",
  "asd00000012",
  "asd00000123"
]

Wyjścia:

[
  "19asd",
  "123asd",
  "12345asd",
  "asd12",
  "asd123"
]
 2
Author: Stephen Quan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-11 01:53:20

Bazując na powyższej odpowiedzi @ Adrien Be i używając kodu, który Brian Huisman & David koelle utworzony, oto zmodyfikowany prototyp sortowania dla tablicy obiektów:

//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"];
  }
}
 1
Author: Eric Norcross,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-16 07:29:52