Javascript: doğal türden alfasayısal dizeler


176

Sayılar ve metin ve bunların bir kombinasyonu oluşan bir dizi sıralamak için en kolay yolu arıyorum.

Örneğin

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

dönüşür

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

Bu, burada sorduğum başka bir sorunun çözümü ile birlikte kullanılacak .

Kendi içinde sıralama işlevi çalışır, ihtiyacım olan '19asd' '123asd' daha küçük olduğunu söyleyebilir bir işlevdir.

Bunu JavaScript ile yazıyorum.

Düzenleme: adormitu'nun işaret ettiği gibi , aradığım şey doğal sıralama için bir işlev


ayrıca bkz How do you do string comparison in JavaScript?. stackoverflow.com/questions/51165/…
Adrien Be

1
Orijinal soru 2010 yılında sordu, bu yüzden şaşırtıcı olmaz :)
ptrn

Yanıtlar:


319

Bu, localeCompare kullanan modern tarayıcılarda artık mümkün. numeric: trueSeçeneği geçerek sayıları akıllıca tanıyacaktır. Tuşlarını kullanarak büyük / küçük harfe duyarlı değilsiniz sensitivity: 'base'. Chrome, Firefox ve IE11'de test edildi.

İşte bir örnek. Dönüyor 1, yani 10 2'den sonra gidiyor:

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

Makale, çok sayıda dizeyi sıralarken performans için şunları söylüyor:

Büyük dizileri sıralama gibi çok sayıda dizeyi karşılaştırırken, bir Intl.Collator nesnesi oluşturmak ve compar özelliği tarafından sağlanan işlevi kullanmak daha iyidir. Dokümanlar bağlantısı

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


12
Bir nesne dizisini sıralamak isterseniz, Collator'ı da kullanabilirsiniz: codepen.io/TimPietrusky/pen/rKzoGN
TimPietrusky

2
Yukarıdaki yorumu netleştirmek için: "locales argümanı sağlanmazsa veya tanımsızsa, çalışma zamanının varsayılan yerel ayarı kullanılır."
gkiely

46

Yani doğal bir türe mi ihtiyacınız var ?

Eğer öyleyse, Brian Huisman'ın David Koelle'nin çalışmasına dayanan bu senaryosu, ihtiyacınız olan şey olabilir.

Görünüşe göre Brian Huisman'ın çözümü artık doğrudan David Koelle'nin blogunda barındırılıyor:


Doğru, doğal sıralama budur. Gönderdiğiniz bağlantıya bakacağım, teşekkürler
ptrn

Bu çok doğal olmayan bir tür. Alphbetic bir tür üretmez.
tchrist

@tchrist: "alfabetik bir tür üretmiyor" ile ne demek istiyorsun?
Adrien Be

İyi çalışıyor ancak negatif sayıları doğru işlemiyor. Yani: ['-1' üretecekti. '-2', '0', '1', '2'].
adrianboimvaser

2
@mhitza Bu kod iyi bir iş yapmak gibi görünüyor github.com/litejs/natural-compare-lite hızlı bir test edin jsbin.com/bevututodavi/1/edit?js,console
Adrien Be

23

Değerleri karşılaştırmak için bir karşılaştırma yöntemi kullanabilirsiniz.

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

Ancak bir diziyi sıralamadaki hız için, sıralamadan önce diziyi donatın, böylece sıralamadaki her adımda yerine küçük harfli dönüşümleri ve normal ifadeyi bir kez yapmanız yeterlidir.

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

bu benim durumumda çalışır, iç dizi dış dizinin sırasına karar verir mi?
ptrn

Nedir String.prototype.tlc()? Bu sizin kendi kodunuz mu yoksa bir yerden mi aldınız? İkincisi ise, lütfen sayfaya bağlantı verin.
Andy E

hata için özür dilerim - düzeltildi, teşekkür ederim. Sıralamayı kontrol etmek için bir [1] ve b [1] istiyorsanız, a = String (a [1]) kullanın. ToLowerCase (); b = Dize (b [1]). toLowerCase ();
kennebec

Sıralamak istediğim verilerin bir listesini aldım, Chrome Dev Tools konsolunda yapmanın kolay olması gerektiğini düşündüm - işlev için teşekkürler!
ajh158

11

Bir dizi nesneniz varsa bunu yapabilirsiniz:

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


2
Mükemmel cevap! Teşekkür ederim.
hubert17

1
Fantastik. Bu benim için de işe yaradı. Thanks @ D0rm1nd0
NITHESHKUMAR R

5

2019'dan itibaren bunu işlemek için en tam özellikli kütüphane doğal düzende görünüyor .

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

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

const ordered = orderBy(unordered)

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

Yalnızca dizelerin dizilerini almakla kalmaz, aynı zamanda bir nesne dizisindeki belirli bir anahtarın değerine göre sıralayabilir. Ayrıca para birimlerini, tarihleri, para birimini ve bir sürü başka şeyi otomatik olarak tanımlayabilir ve sıralayabilir.

Şaşırtıcı bir şekilde, gziplendiğinde sadece 1.6kB.


2

Dönüştüren 8 basamaklı bir dolgu işlevi düşünün:

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

Yastıklı dizeleri, '123asd'den önce görünecek şekilde' 19asd 'sıralamamıza yardımcı olmak için kullanabiliriz.

/\d+/gDoldurulması gereken tüm sayıları bulmanıza yardımcı olması için normal ifadeyi kullanın :

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

Aşağıda bu tekniği kullanarak sıralama gösterilmektedir:

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

Ara sonuçlar, natural_expand () yordamının ne yaptığını gösterir ve sonraki natural_compare yordamının nasıl çalışacağını anlamanızı sağlar:

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

Çıktılar:

[
  "19asd",
  "123asd",
  "12345asd",
  "asd12",
  "asd123"
]

1

@Adrien Be'nin cevabını temel alarak ve şu kodu kullanarak Brian Huisman ve David koelle'nin oluşturduğu bir dizi nesne için değiştirilmiş bir prototip sıralaması:

//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"];
  }
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.