Javascript: natürliche Art von alphanumerischen Zeichenfolgen

173

Ich suche nach dem einfachsten Weg, ein Array zu sortieren, das aus Zahlen und Text besteht, und eine Kombination davon.

Z.B

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

verwandelt sich in

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

Dies wird in Kombination mit der Lösung einer anderen Frage verwendet, die ich hier gestellt habe .

Die Sortierfunktion an sich funktioniert. Was ich brauche, ist eine Funktion, die sagen kann, dass '19asd' kleiner als '123asd' ist.

Ich schreibe dies in JavaScript.

Bearbeiten: Wie Adormitu betonte, suche ich nach einer Funktion für die natürliche Sortierung

ptrn
quelle
siehe auch How do you do string comparison in JavaScript?auf stackoverflow.com/questions/51165/…
Adrien Be
1
Die ursprüngliche Frage wurde im Jahr 2010 gestellt, so wäre es nicht überraschend :)
ptrn
Mögliches Duplikat von Wie man Strings in JavaScript
sortiert

Antworten:

314

Dies ist jetzt in modernen Browsern mit localeCompare möglich. Durch Übergeben der numeric: trueOption werden Zahlen intelligent erkannt. Sie können die Groß- und Kleinschreibung nicht berücksichtigen sensitivity: 'base'. Getestet in Chrome, Firefox und IE11.

Hier ist ein Beispiel. Es kehrt zurück 1, was bedeutet, dass 10 nach 2 geht:

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

Zur Leistung beim Sortieren einer großen Anzahl von Zeichenfolgen heißt es in dem Artikel:

Wenn Sie eine große Anzahl von Zeichenfolgen vergleichen, z. B. beim Sortieren großer Arrays, ist es besser, ein Intl.Collator-Objekt zu erstellen und die durch die compare-Eigenschaft bereitgestellte Funktion zu verwenden. 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));

frodo2975
quelle
12
Wenn Sie ein Array von Objekten sortieren möchten, können Sie auch den Collator
TimPietrusky
2
Zur Verdeutlichung des obigen Kommentars: "Wenn das Argument locales nicht angegeben oder undefiniert ist, wird das Standardgebietsschema der Laufzeit verwendet."
Gkiely
46

Sie brauchen also eine natürliche Sorte ?

Wenn ja, dann ist dieses Drehbuch von Brian Huisman, das auf David Koelles Arbeit basiert , vielleicht genau das , was Sie brauchen.

Es scheint, als ob Brian Huismans Lösung jetzt direkt auf David Koelles Blog gehostet wird:

mhitza
quelle
Richtige, natürliche Sorte ist das, wonach ich suche. Ich werde in den Link schauen, den Sie gesendet haben, danke
ptrn
Das ist eine sehr unnatürliche Art. Es wird keine alphabetische Sorte erzeugt.
Tchrist
@tchrist: Was meinst du mit "es erzeugt keine alphabetische Sortierung?"
Adrien Be
Es funktioniert gut, aber es behandelt negative Zahlen nicht richtig. Dh: es würde ['-1' produzieren. '-2', '0', '1', '2'].
Adrianboimvaser
2
@mhitza dieser Code scheint einen guten Job zu machen github.com/litejs/natural-compare-lite siehe einen schnellen Test jsbin.com/bevututodavi/1/edit?js,console
Adrien Be
23

Um Werte zu vergleichen, können Sie eine Vergleichsmethode verwenden.

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

Um das Sortieren eines Arrays zu beschleunigen, müssen Sie das Array vor dem Sortieren manipulieren, sodass Sie die Konvertierungen in Kleinbuchstaben und den regulären Ausdruck nur einmal anstatt in jedem Schritt der Sortierung durchführen müssen.

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];
    }
}
kennebec
quelle
Würde dies in meinem Fall funktionieren, wenn das innere Array die Reihenfolge des äußeren bestimmt?
10.
Was ist String.prototype.tlc()? Ist das dein eigener Code oder hast du ihn von irgendwoher bekommen? Wenn letzteres der Fall ist, verlinken Sie bitte auf die Seite.
Andy E
Entschuldigung für den Fehler korrigiert, danke. Wenn Sie möchten, dass a [1] und b [1] die Sortierung steuern, verwenden Sie a = String (a [1]). ToLowerCase (); b = String (b [1]). toLowerCase ();
Kennebec
Ich hatte gerade eine Liste mit Daten, die ich sortieren wollte, dachte, es sollte einfach sein, dies in der Chrome Dev Tools-Konsole zu tun - danke für die Funktion!
Ajh158
9

Wenn Sie eine Reihe von Objekten haben, können Sie Folgendes tun:

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

D0rm1nd0
quelle
1
Perfekte Antwort! Danke dir.
Hubert17
5

Die ab 2019 am besten ausgestattete Bibliothek, um dies zu handhaben, scheint natürlich zu sein .

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

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

const ordered = orderBy(unordered)

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

Es werden nicht nur Arrays von Zeichenfolgen benötigt, sondern es kann auch nach dem Wert eines bestimmten Schlüssels in einem Array von Objekten sortiert werden. Es kann auch automatisch Zeichenfolgen von Währungen, Daten, Währungen und einer Reihe anderer Dinge identifizieren und sortieren.

Überraschenderweise sind es auch nur 1,6 KB, wenn sie gezippt werden.

Julien
quelle
2

Stellen Sie sich eine 8-stellige Auffüllfunktion vor, die Folgendes transformiert:

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

Wir können die gepolsterten Zeichenfolgen verwenden, um '19asd' vor '123asd' zu sortieren.

Verwenden Sie den regulären Ausdruck /\d+/g, um alle Zahlen zu finden, die aufgefüllt werden müssen:

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

Das Folgende zeigt das Sortieren mit dieser Technik:

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

Die Zwischenergebnisse zeigen, was die Routine natural_expand () tut, und geben Ihnen ein Verständnis dafür, wie die nachfolgende Routine natural_compare funktioniert:

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

Ausgänge:

[
  "19asd",
  "123asd",
  "12345asd",
  "asd12",
  "asd123"
]
Stephen Quan
quelle
1

Aufbauend auf der obigen Antwort von @Adrien Be und unter Verwendung des von Brian Huisman & David koelle erstellten Codes ist hier eine modifizierte Prototypsortierung für eine Reihe von Objekten:

//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"];
  }
}
Eric Norcross
quelle