Come ordinare le stringhe in JavaScript


344

Ho un elenco di oggetti che desidero ordinare in base a un campo attrdi tipo stringa. Ho provato a usare-

list.sort(function (a, b) {
    return a.attr - b.attr
})

ma -ho scoperto che non sembra funzionare con le stringhe in JavaScript. Come posso ordinare un elenco di oggetti basato su un attributo con tipo stringa?


1
vedi JavaScript case insensitive string comparisonsu stackoverflow.com/questions/2140627/…
Adrien, il

Per una rapida soluzione "internazionalizzata" (suppongo che solo in parte ciò non copra tutti gli accenti del mondo), potresti semplicemente ignorare gli accenti, cioè rimuoverli. Quindi fai solo il confronto delle stringhe, vedi Javascript : remove accents/diacritics in stringssu stackoverflow.com/questions/990904/…
Adrien Be

2
Abbastanza divertente Jeff Atwood stesso ha scritto un post sul blog su questo problema comune nel 2007, vedi blog.codinghorror.com/sorting-for-humans-natural-sort-order
Adrien Be

Risposte:


622

Usa String.prototype.localeCompareun per il tuo esempio:

list.sort(function (a, b) {
    return ('' + a.attr).localeCompare(b.attr);
})

Forziamo a.attr come stringa per evitare eccezioni. localeCompareè supportato da Internet Explorer 6 e Firefox 1. È possibile che venga visualizzato il seguente codice che non rispetta le impostazioni internazionali:

if (item1.attr < item2.attr)
  return -1;
if ( item1.attr > item2.attr)
  return 1;
return 0;

81
Prima che qualcuno commetta lo stesso errore frettoloso che ho fatto, è local e Compare, non localCompare.
ento

12
La prima soluzione prenderà in considerazione "A" dopo "z" ma prima di "Z", poiché sta effettuando un confronto sul valore ASCII del carattere. localeCompare()non si verifica questo problema ma non comprende i numeri, quindi otterrai ["1", "10", "2"] come con i confronti di ordinamento nella maggior parte delle lingue. se si desidera l'ordinamento per il front-end di interfaccia utente, sguardo al alphanum / tipo naturale algoritmo stackoverflow.com/questions/4340227/... o stackoverflow.com/questions/4321829/...
Dead.Rabit

2
Si noti che localeCompare()è supportato solo nei browser moderni: IE11 + al momento della scrittura, vedere developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Adrien Be

3
No, intendo la prima riga della tabella, @Adrien - IE supporta il localeCompare()ritorno di molte versioni, ma non supporta la specifica delle impostazioni locali fino alla versione 11. Nota anche le domande a cui Dead.Rabit ha collegato.
Shog9,

3
@ Shog9 mio male, sembra che sia supportato da IE6! vedere (scorrere verso il basso / cercare il metodo localeCompare ()) su msdn.microsoft.com/en-us/library/ie/s4esdbwz(v=vs.94).aspx . Una cosa da notare, tuttavia, nelle vecchie implementazioni in cui non utilizziamo gli argomenti locales e options (quello usato prima di IE11), la localizzazione e l'ordinamento utilizzati dipendono interamente dall'implementazione , in altre parole: Firefox, Safari, Chrome e IE lo fanno NON ordinare le stringhe nello stesso ordine. vedi code.google.com/p/v8/issues/detail?id=459
Adrien Be

166

Una risposta aggiornata (ottobre 2014)

Ero davvero seccato per questo ordinamento naturale delle stringhe, quindi ho impiegato parecchio tempo per indagare su questo problema. Spero che aiuti.

Per farla breve

localeCompare()il supporto del personaggio è tosto, basta usarlo. Come sottolineato da Shog9, la risposta alla tua domanda è:

return item1.attr.localeCompare(item2.attr);

Bug trovati in tutte le implementazioni personalizzate di "ordinamento di stringhe naturali" javascript

Ci sono un sacco di implementazioni personalizzate là fuori, cercando di fare un confronto di stringhe più precisamente chiamato "ordinamento di stringhe naturali"

Quando "giocavo" con queste implementazioni, ho sempre notato una strana scelta "ordinamento naturale", o piuttosto errori (o omissioni nei casi migliori).

In genere, i caratteri speciali (spazio, trattino, e commerciale, parentesi e così via) non vengono elaborati correttamente.

Li troverai quindi apparire confusi in luoghi diversi, in genere che potrebbero essere:

  • alcuni saranno tra la "Z" maiuscola e la "a" minuscola
  • alcuni saranno tra '9' e 'A' maiuscoli
  • alcuni saranno dopo 'z' minuscola

Quando ci si sarebbe aspettati che tutti i personaggi speciali fossero "raggruppati" in un unico posto, tranne forse per il carattere speciale spaziale (che sarebbe sempre il primo personaggio). Cioè, tutti prima dei numeri o tutti tra numeri e lettere (le lettere minuscole e maiuscole sono "insieme" una dopo l'altra) o tutte dopo le lettere.

La mia conclusione è che tutti non riescono a fornire un ordine coerente quando inizio ad aggiungere caratteri quasi insoliti (ad es. Personaggi con segni diacritici o caratteri come trattino, punto esclamativo e così via).

Ricerca sulle implementazioni personalizzate:

Implementazioni "ordinamento naturale delle stringhe" native dei browser tramite localeCompare()

localeCompare()l'implementazione meno recente (senza argomenti locali e opzioni) è supportata da IE6 +, vedere http://msdn.microsoft.com/en-us/library/ie/s4esdbwz(v=vs.94).aspx (scorrere fino a localeCompare ( ) metodo). Il localeCompare()metodo integrato fa un lavoro molto migliore nell'ordinamento, anche con caratteri internazionali e speciali. L'unico problema nell'uso del localeCompare()metodo è che "le impostazioni locali e l'ordinamento utilizzate dipendono interamente dall'implementazione". In altre parole, quando si utilizza localeCompare come stringOne.localeCompare (stringTwo): Firefox, Safari, Chrome e IE hanno un diverso ordinamento per Stringhe.

Ricerca sulle implementazioni native del browser:

Difficoltà di "ordinamento per stringa naturale"

L'implementazione di un solido algoritmo (che significa: coerente ma copre anche una vasta gamma di caratteri) è un compito molto difficile. UTF8 contiene più di 2000 caratteri e copre più di 120 script (lingue) . Infine, ci sono alcune specifiche per queste attività, si chiama "Unicode Collation Algorithm", che può essere trovato su http://www.unicode.org/reports/tr10/ . Puoi trovare maggiori informazioni a riguardo su questa domanda che ho pubblicato /software/257286/is-there-any-language-agnostic-specification-for-string-natural-sorting-order

Conclusione finale

Quindi, considerando l'attuale livello di supporto fornito dalle implementazioni personalizzate javascript che ho riscontrato, probabilmente non vedremo mai nulla avvicinarsi al supporto di tutti questi personaggi e script (lingue). Quindi preferirei utilizzare il metodo localeCompare () nativo del browser. Sì, ha il rovescio della medaglia di essere incoerente tra i browser, ma i test di base mostrano che copre una gamma molto più ampia di caratteri, consentendo ordinamenti solidi e significativi.

Quindi, come sottolineato da Shog9, la risposta alla tua domanda è:

return item1.attr.localeCompare(item2.attr);

Ulteriori letture:

Grazie alla bella risposta di Shog9, che mi ha messo nella direzione "giusta" credo


38

Risposta (in Modern ECMAScript)

list.sort((a, b) => (a.attr > b.attr) - (a.attr < b.attr))

O

list.sort((a, b) => +(a.attr > b.attr) || -(a.attr < b.attr))

Descrizione

Il cast di un valore booleano su un numero produce quanto segue:

  • true -> 1
  • false -> 0

Considera tre possibili modelli:

  • x è maggiore di y: (x > y) - (y < x)-> 1 - 0->1
  • x è uguale a y: (x > y) - (y < x)-> 0 - 0->0
  • x è più piccola di y: (x > y) - (y < x)-> 0 - 1->-1

(Alternativa)

  • x è maggiore di y: +(x > y) || -(x < y)-> 1 || 0->1
  • x è uguale a y: +(x > y) || -(x < y)-> 0 || 0->0
  • x è più piccola di y: +(x > y) || -(x < y)-> 0 || -1->-1

Quindi queste logiche equivalgono alle tipiche funzioni del comparatore di ordinamento.

if (x == y) {
    return 0;
}
return x > y ? 1 : -1;

1
Come ho commentato la risposta precedente che utilizzava questo trucco, le risposte di solo codice possono essere rese più utili spiegando come funzionano.
Dan Dascalescu,

Descrizione aggiunta
mpyw il

Puoi commentare se questo è meglio o peggio di localeCompare?
Ha funzionato Lottem il

3
@RanLottem localeComparee il confronto standard producono risultati diversi. Quale ti aspetti? ["A", "b", "C", "d"].sort((a, b) => a.localeCompare(b))ordina in ordine alfabetico senza distinzione tra maiuscole e minuscole mentre ["A", "b", "C", "d"].sort((a, b) => (a > b) - (a < b))fa in ordine di punti di
codice

Vedo, quello sembra essere un punto critico. Qualche idea sulle differenze di prestazioni?
Ran Lottem,

13

Dovresti usare> o <e == qui. Quindi la soluzione sarebbe:

list.sort(function(item1, item2) {
    var val1 = item1.attr,
        val2 = item2.attr;
    if (val1 == val2) return 0;
    if (val1 > val2) return 1;
    if (val1 < val2) return -1;
});

1
In una nota a margine, questo non gestirà confronti tra stringhe e numeri. Ad esempio: 'Z' <9 (false), 'Z'> 9 (anche false ??), 'Z' == 9 (anche false !!). Silly NaN in JavaScript ...
Kato

7

Funzione freccia ternaria nidificata

(a,b) => (a < b ? -1 : a > b ? 1 : 0)

7

poiché le stringhe possono essere confrontate direttamente in javascript, questo farà il lavoro

list.sort(function (a, b) {
    return a.attr > b.attr ? 1: -1;
})

la sottrazione in una funzione di ordinamento viene utilizzata solo quando si desidera un ordinamento non alfabetico (numerico) e, naturalmente, non funziona con le stringhe


6

Ero stato infastidito a lungo per questo, quindi alla fine ho cercato questo e ti ho dato questa ragione lunga e tortuosa del perché le cose sono come sono.

Dalle specifiche :

Section 11.9.4   The Strict Equals Operator ( === )

The production EqualityExpression : EqualityExpression === RelationalExpression
is evaluated as follows: 
- Let lref be the result of evaluating EqualityExpression.
- Let lval be GetValue(lref).
- Let rref be the result of evaluating RelationalExpression.
- Let rval be GetValue(rref).
- Return the result of performing the strict equality comparison 
  rval === lval. (See 11.9.6)

Quindi ora andiamo a 11.9.6

11.9.6   The Strict Equality Comparison Algorithm

The comparison x === y, where x and y are values, produces true or false. 
Such a comparison is performed as follows: 
- If Type(x) is different from Type(y), return false.
- If Type(x) is Undefined, return true.
- If Type(x) is Null, return true.
- If Type(x) is Number, then
...
- If Type(x) is String, then return true if x and y are exactly the 
  same sequence of characters (same length and same characters in 
  corresponding positions); otherwise, return false.

Questo è tutto. L'operatore triple egals applicato alle stringhe restituisce true se gli argomenti sono esattamente le stesse stringhe (stessa lunghezza e stessi caratteri nelle posizioni corrispondenti).

Quindi ===funzionerà nei casi in cui stiamo provando a confrontare stringhe che potrebbero essere arrivate da fonti diverse, ma che sappiamo che alla fine avranno gli stessi valori - uno scenario abbastanza comune per le stringhe incorporate nel nostro codice. Ad esempio, se abbiamo una variabile denominata connection_statee desideriamo sapere in quale momento si trova uno dei seguenti stati ['connecting', 'connected', 'disconnecting', 'disconnected'], possiamo usare direttamente il=== .

Ma c'è di più. Poco sopra l'11.9.4, c'è una breve nota:

NOTE 4     
  Comparison of Strings uses a simple equality test on sequences of code 
  unit values. There is no attempt to use the more complex, semantically oriented
  definitions of character or string equality and collating order defined in the 
  Unicode specification. Therefore Strings values that are canonically equal
  according to the Unicode standard could test as unequal. In effect this 
  algorithm assumes that both Strings are already in normalized form.

Hmm. E adesso? Le stringhe ottenute esternamente possono, e molto probabilmente lo saranno, un unicodey strano, e il nostro gentile ===non gli farà giustizia. In viene localeComparein soccorso:

15.5.4.9   String.prototype.localeCompare (that)
    ...
    The actual return values are implementation-defined to permit implementers 
    to encode additional information in the value, but the function is required 
    to define a total ordering on all Strings and to return 0 when comparing
    Strings that are considered canonically equivalent by the Unicode standard. 

Ora possiamo andare a casa.

tl; dr;

Per confrontare le stringhe in javascript, utilizzare localeCompare; se sai che le stringhe non hanno componenti non ASCII perché sono, ad esempio, costanti di programma interne, allora ===funziona anche.


0

Nella tua operazione nella domanda iniziale, stai eseguendo la seguente operazione:

item1.attr - item2.attr

Quindi, supponendo che siano numeri (cioè item1.attr = "1", item2.attr = "2") È comunque possibile utilizzare l'operatore "===" (o altri valutatori rigorosi) purché si garantisca il tipo. Quanto segue dovrebbe funzionare:

return parseInt(item1.attr) - parseInt(item2.attr);

Se sono alphaNumeric, utilizzare localCompare ().


0
list.sort(function(item1, item2){
    return +(item1.attr > item2.attr) || +(item1.attr === item2.attr) - 1;
}) 

Come funzionano i campioni:

+('aaa'>'bbb')||+('aaa'==='bbb')-1
+(false)||+(false)-1
0||0-1
-1

+('bbb'>'aaa')||+('bbb'==='aaa')-1
+(true)||+(false)-1
1||0-1
1

+('aaa'>'aaa')||+('aaa'==='aaa')-1
+(false)||+(true)-1
0||1-1
0

3
Le risposte di solo codice possono essere rese più utili spiegando come funzionano.
Dan Dascalescu,

-2
<!doctype html>
<html>
<body>
<p id = "myString">zyxtspqnmdba</p>
<p id = "orderedString"></p>
<script>
var myString = document.getElementById("myString").innerHTML;
orderString(myString);
function orderString(str) {
    var i = 0;
    var myArray = str.split("");
    while (i < str.length){
        var j = i + 1;
        while (j < str.length) {
            if (myArray[j] < myArray[i]){
                var temp = myArray[i];
                myArray[i] = myArray[j];
                myArray[j] = temp;
            }
            j++;
        }
        i++;
    }
    var newString = myArray.join("");
    document.getElementById("orderedString").innerHTML = newString;
}
</script>
</body>
</html>

1
Si prega di aggiungere alcune informazioni su come questo risolverà la domanda alla tua risposta. Le risposte di solo codice non sono benvenute. Grazie.
wayneOS,

qui vuoi ordinare i caratteri all'interno di una stringa, che non è quello che ti viene chiesto. Puoi ottenere questo ordinamento semplicemente usando "Array.sort" ad esempio str.split (""). Sort () .join ("")
Alejadro Xalabarder

-2
var str = ['v','a','da','c','k','l']
var b = str.join('').split('').sort().reverse().join('')
console.log(b)

Sebbene questo codice possa risolvere la domanda, inclusa una spiegazione di come e perché questo risolva il problema, aiuterebbe davvero a migliorare la qualità del tuo post e probabilmente comporterebbe più voti positivi. Ricorda che stai rispondendo alla domanda per i lettori in futuro, non solo per la persona che chiede ora. per favore modificare la risposta di aggiungere una spiegazione, e dare un'indicazione di ciò si applicano le limitazioni e le assunzioni.
Dave,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.