Mam tablicę ciągów, które muszę sortować w JavaScript, ale bez rozróżniania wielkości liter. Jak to zrobić?
Mam tablicę ciągów, które muszę sortować w JavaScript, ale bez rozróżniania wielkości liter. Jak to zrobić?
Odpowiedzi:
W (prawie :) jeden-liniowiec
["Foo", "bar"].sort(function (a, b) {
return a.toLowerCase().localeCompare(b.toLowerCase());
});
Co skutkuje w
[ 'bar', 'Foo' ]
Podczas
["Foo", "bar"].sort();
prowadzi do
[ 'Foo', 'bar' ]
return a.localeCompare(b, 'en', {'sensitivity': 'base'});
toLowerCase()
gdy localeCompare
już to robi domyślnie w niektórych przypadkach. Możesz przeczytać więcej o parametrach, które należy przekazać tutaj: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
myArray.sort(
function(a, b) {
if (a.toLowerCase() < b.toLowerCase()) return -1;
if (a.toLowerCase() > b.toLowerCase()) return 1;
return 0;
}
);
EDYCJA: Należy pamiętać, że pierwotnie napisałem to w celu zilustrowania techniki, a nie mając na uwadze wydajność. Bardziej kompaktowe rozwiązanie można znaleźć w odpowiedzi na @Ivan Krechetov.
toLowerCase
dwa razy na każdym ciągu; byłoby bardziej wydajne do przechowywania obniżonych wersji łańcucha w zmiennych.
.toLowerCase()
wiele razy dla każdego elementu w tablicy. Na przykład 45 wywołań funkcji porównywania podczas sortowania 10 pozycji w odwrotnej kolejności. var i = 0; ["z","y","x","w","v","u","t","s","r","q"].sort(function (a, b) {++i; return a.toLowerCase().localeCompare(b.toLowerCase());}); console.log("Calls to Compare: " + i); // i === 45
Czas wrócić do tego starego pytania.
Nie powinieneś używać rozwiązań opartych na toLowerCase
. Są nieefektywne i po prostu nie działają w niektórych językach (na przykład tureckim). Wolę to:
['Foo', 'bar'].sort((a, b) => a.localeCompare(b, undefined, {sensitivity: 'base'}))
Sprawdź w dokumentacji kompatybilność przeglądarki i wszystko, co musisz wiedzieć o tej sensitivity
opcji.
arr.sort(function(a,b) {
a = a.toLowerCase();
b = b.toLowerCase();
if (a == b) return 0;
if (a > b) return 1;
return -1;
});
return a === b ? 0 : a > b ? 1 : -1;
["111", "33"]
, możemy chcieć, aby zwrócił, ["111", "33"]
ponieważ 1 występuje przed 3 w kolejności kodów znaków. Jednak funkcja w tej odpowiedzi zostanie zwrócona, ["33", "111"]
ponieważ liczba 33
jest mniejsza niż liczba 111
.
"33" > "111" === true
i 33 > 111 === false
. Działa zgodnie z przeznaczeniem.
Możesz także użyć nowego Intl.Collator().compare
, według MDN, jest to bardziej wydajne podczas sortowania tablic. Minusem jest to, że nie jest obsługiwany przez starsze przeglądarki. MDN stwierdza, że w ogóle nie jest obsługiwany w Safari. Musisz to zweryfikować, ponieważ stwierdza, że Intl.Collator
jest obsługiwane.
Podczas porównywania dużej liczby ciągów, na przykład podczas sortowania dużych tablic, lepiej jest utworzyć obiekt Intl.Collator i użyć funkcji zapewnianej przez jego właściwość porównywania
["Foo", "bar"].sort(Intl.Collator().compare); //["bar", "Foo"]
Jeśli chcesz zagwarantować tę samą kolejność niezależnie od kolejności elementów w tablicy wejściowej, oto stabilne sortowanie:
myArray.sort(function(a, b) {
/* Storing case insensitive comparison */
var comparison = a.toLowerCase().localeCompare(b.toLowerCase());
/* If strings are equal in case insensitive comparison */
if (comparison === 0) {
/* Return case sensitive comparison instead */
return a.localeCompare(b);
}
/* Otherwise return result */
return comparison;
});
Normalizuj obudowę za .sort()
pomocą .toLowerCase()
.
Możesz także użyć operatora Elvisa:
arr = ['Bob', 'charley', 'fudge', 'Fudge', 'biscuit'];
arr.sort(function(s1, s2){
var l=s1.toLowerCase(), m=s2.toLowerCase();
return l===m?0:l>m?1:-1;
});
console.log(arr);
Daje:
biscuit,Bob,charley,fudge,Fudge
Metoda localeCompare jest prawdopodobnie w porządku ...
Uwaga: operator Elvisa jest skrótem od „operatora trójskładnikowego”, ponieważ w innym przypadku, zwykle z przypisaniem.
Jeśli spojrzysz na?: Z boku, wygląda jak Elvis ...
tzn. Zamiast:
if (y) {
x = 1;
} else {
x = 2;
}
możesz użyć:
x = y?1:2;
tzn. gdy y jest prawdziwe, to zwróć 1 (dla przypisania do x), w przeciwnym razie zwróć 2 (dla przypisania do x).
x = y ? y : z
możesz to zrobić x = y ?: z
. JavaScript nie ma faktycznego operatora Elvisa, ale możesz używać go x = y || z
w podobny sposób.
Inne odpowiedzi zakładają, że tablica zawiera łańcuchy. Moja metoda jest lepsza, ponieważ będzie działać, nawet jeśli tablica zawiera null, undefined lub inne nie łańcuchy.
var notdefined;
var myarray = ['a', 'c', null, notdefined, 'nulk', 'BYE', 'nulm'];
myarray.sort(ignoreCase);
alert(JSON.stringify(myarray)); // show the result
function ignoreCase(a,b) {
return (''+a).toUpperCase() < (''+b).toUpperCase() ? -1 : 1;
}
null
Zostaną posortowane między „” i „nulk nulm”. Ale zawszeundefined
będą sortowane na końcu.
(''+notdefined) === "undefined"
więc sortowałoby przed „z”
Array.prototype.sort
: | ponieważ część o (''+notdefined) === "undefined"
naprawdę jest prawdziwa ... co oznacza, że jeśli odwrócisz -1 i 1 w funkcji sortowania, aby odwrócić kolejność, niezdefiniowane nadal będzie sortowane do końca. Należy również wziąć to pod uwagę, gdy używasz funkcji porównania poza kontekstem sortowania tablicowego (tak jak wtedy, gdy natrafiłem na to pytanie).
Array.prototype.sort
definicji - jeszcze kilka komentarzy. Po pierwsze, nie ma potrzeby (''+a)
- ECMAScript wymaga toString()
wywołania elementów przed przekazaniem ich do CompareFn. Po drugie, fakt, że ignoreCase
zwraca się 1
przy porównywaniu ciągów równych (w tym równych dla każdego przypadku) oznacza, że specyfikacja nie definiuje wyniku, jeśli istnieją zduplikowane wartości (myślę, że prawdopodobnie będzie w porządku tylko z kilkoma niepotrzebnymi zamianami, jak sądzę).
undefined
jest to szczególny przypadek, który dla każdego x x <undefined i x> undefined są fałszywe . To undefined
jest zawsze ostatnie, jest produktem ubocznym sortowania. Próbowałem zmienić ('' + a) na po prostu a, ale to się nie udaje. ja dostać TypeError: a.toUpperCase is not a function
. Najwyraźniej nietoString
jest wywoływany przed wywołaniem funkcji porównawczej.
undefined
porównania Fn nigdy nie
Wersja ES6:
["Foo", "bar"].sort((a, b) => a.localeCompare(b, 'en', { sensitivity: 'base' }))
Na poparcie przyjętej odpowiedzi chciałbym dodać, że funkcja poniżej wydaje się zmieniać wartości w oryginalnej tablicy, która ma być sortowana, tak że nie tylko sortuje małe litery, ale wartości wielkich liter zostaną również zmienione na małe litery. Jest to dla mnie problem, ponieważ chociaż chcę widzieć Maryję obok Maryi, nie chcę, aby przypadek pierwszej wartości Mary został zmieniony na małe.
myArray.sort(
function(a, b) {
if (a.toLowerCase() < b.toLowerCase()) return -1;
if (a.toLowerCase() > b.toLowerCase()) return 1;
return 0;
}
);
W moich eksperymentach następująca funkcja z zaakceptowanej odpowiedzi sortuje poprawnie, ale nie zmienia wartości.
["Foo", "bar"].sort(function (a, b) {
return a.toLowerCase().localeCompare(b.toLowerCase());
});
Może to pomóc, jeśli masz problemy ze zrozumieniem:
var array = ["sort", "Me", "alphabetically", "But", "Ignore", "case"];
console.log('Unordered array ---', array, '------------');
array.sort(function(a,b) {
a = a.toLowerCase();
b = b.toLowerCase();
console.log("Compare '" + a + "' and '" + b + "'");
if( a == b) {
console.log('Comparison result, 0 --- leave as is ');
return 0;
}
if( a > b) {
console.log('Comparison result, 1 --- move '+b+' to before '+a+' ');
return 1;
}
console.log('Comparison result, -1 --- move '+a+' to before '+b+' ');
return -1;
});
console.log('Ordered array ---', array, '------------');
// return logic
/***
If compareFunction(a, b) is less than 0, sort a to a lower index than b, i.e. a comes first.
If compareFunction(a, b) returns 0, leave a and b unchanged with respect to each other, but sorted with respect to all different elements. Note: the ECMAscript standard does not guarantee this behaviour, and thus not all browsers (e.g. Mozilla versions dating back to at least 2003) respect this.
If compareFunction(a, b) is greater than 0, sort b to a lower index than a.
***/
arr.sort(function(a,b) {
a = a.toLowerCase();
b = b.toLowerCase();
if( a == b) return 0;
if( a > b) return 1;
return -1;
});
W powyższej funkcji, jeśli po prostu porównamy, gdy dwie małe litery a i b mają wartość, nie uzyskamy ładnego wyniku.
Przykład: jeśli tablica to [A, a, B, b, c, C, D, d, e, E] i korzystamy z powyższej funkcji, mamy dokładnie tę tablicę. Nic się nie zmieniło.
Aby uzyskać wynik [A, a, B, b, C, c, D, d, E, e], powinniśmy porównać ponownie, gdy dwie małe litery są równe:
function caseInsensitiveComparator(valueA, valueB) {
var valueALowerCase = valueA.toLowerCase();
var valueBLowerCase = valueB.toLowerCase();
if (valueALowerCase < valueBLowerCase) {
return -1;
} else if (valueALowerCase > valueBLowerCase) {
return 1;
} else { //valueALowerCase === valueBLowerCase
if (valueA < valueB) {
return -1;
} else if (valueA > valueB) {
return 1;
} else {
return 0;
}
}
}
Górną odpowiedź zapakowałem w polifill, aby móc wywoływać funkcję .sortIgnoreCase () na tablicach łańcuchów
// Array.sortIgnoreCase() polyfill
if (!Array.prototype.sortIgnoreCase) {
Array.prototype.sortIgnoreCase = function () {
return this.sort(function (a, b) {
return a.toLowerCase().localeCompare(b.toLowerCase());
});
};
}
Owiń swoje sznurki / /i
. Jest to prosty sposób na użycie wyrażenia regularnego do zignorowania obudowy