Jak przeprowadzić sortowanie bez rozróżniania wielkości liter w JavaScript?

220

Mam tablicę ciągów, które muszę sortować w JavaScript, ale bez rozróżniania wielkości liter. Jak to zrobić?

Jérôme Verstrynge
źródło

Odpowiedzi:

404

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' ]
Ivan Krechetov
źródło
9
Pamiętaj, że zaawansowane opcje localeCompare nie są jeszcze obsługiwane na wszystkich platformach / przeglądarkach. Wiem, że nie są one używane w tym przykładzie, ale po prostu chciałem dodać dla jasności. Aby uzyskać więcej informacji, zobacz MDN
Ayame__
97
Jeśli zamierzasz włączyć funkcję localeCompare (), możesz po prostu użyć jej zdolności do rozróżniania wielkości liter, np .:return a.localeCompare(b, 'en', {'sensitivity': 'base'});
Michael Dyck
2
+1 za brak połączenia, toLowerCase()gdy localeComparejuż 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/…
Milimetric
3
@Milimetryczny zgodnie ze wskazaną stroną, ta funkcja nie jest obsługiwana przez niektóre przeglądarki (np. IE <11 lub Safari). rozwiązanie wspomniane tutaj jest bardzo dobre, ale nadal wymagałoby backportowania / wypełniania zawartości w niektórych przeglądarkach.
3k-
2
Jeśli masz dużą tablicę, warto zastosować ją w items.sort(new Intl.Collator('en').compare)celu uzyskania lepszej wydajności. (Patrz MDN .)
valtlai
60
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.

Ron Tornambe
źródło
3
Może to wywoływać toLowerCasedwa razy na każdym ciągu; byłoby bardziej wydajne do przechowywania obniżonych wersji łańcucha w zmiennych.
Jakub
Prawda i dzięki. Napisałem to z myślą o jasności, a nie o wydajności. Chyba powinienem to zauważyć.
ron tornambe,
1
@Jacob Aby być uczciwym, zaakceptowana odpowiedź ma ten sam podstawowy problem: może wywoływać .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
nic nie jest potrzebne
47

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 sensitivityopcji.

ZunTzu
źródło
1
Uważaj, nie jest to obsługiwane we wszystkich silnikach javascript.
Luboš Turek
26
arr.sort(function(a,b) {
    a = a.toLowerCase();
    b = b.toLowerCase();
    if (a == b) return 0;
    if (a > b) return 1;
    return -1;
});
Niet the Dark Absol
źródło
1
lubreturn a === b ? 0 : a > b ? 1 : -1;
Devin G Rhode,
Prawdopodobnie nie będzie to działać zgodnie z przeznaczeniem dla ciągów reprezentujących liczby. Operatory arytmetyczne będą używać semantyki liczb zamiast ciągów znaków. Na przykład, jeśli tak ["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 33jest mniejsza niż liczba 111.
Austin Davis,
@AustinDavis "33" > "111" === truei 33 > 111 === false. Działa zgodnie z przeznaczeniem.
Niet the Dark Absol
12

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.Collatorjest 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"]
mateuscb
źródło
11

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;
});
Aalex Gabi
źródło
5

Normalizuj obudowę za .sort()pomocą .toLowerCase().


źródło
4

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).

AndyS
źródło
5
Mówiąc pedantycznie, to nie jest operator Elvisa. To tylko podstawowy operator trójskładnikowy. Prawdziwym operatorem Elvisa jest łączenie zerowe, np. Zamiast tego x = y ? y : zmożesz to zrobić x = y ?: z. JavaScript nie ma faktycznego operatora Elvisa, ale możesz używać go x = y || zw podobny sposób.
Charles Wood
3

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

nullZostaną posortowane między „” i „nulk nulm”. Ale zawszeundefined będą sortowane na końcu.

John Henckel
źródło
(''+notdefined) === "undefined"więc sortowałoby przed „z”
MattW
Chyba powinienem był sprawdzić definicję 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).
MattW
Po rozważeniu tej Array.prototype.sortdefinicji - 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 ignoreCasezwraca się 1przy 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ę).
MattW
@MattW, Wydaje mi się, że undefinedjest to szczególny przypadek, który dla każdego x x <undefined i x> undefined są fałszywe . To undefinedjest 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.
John Henckel
1
Ach, ok, to ma sens. Dla undefinedporównania Fn nigdy nie
John Henckel
1

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());
});
John Shearing
źródło
0

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.
***/

http://jsfiddle.net/ianjamieson/wmxn2ram/1/

Ian Jamieson
źródło
0
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;
        }
    }
}
Zazdrość
źródło
-1

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());
        });
    };
}
Jason
źródło
Proszę, nigdy tego nie rób. Modyfikuj tylko prototyp rzeczy, które posiadasz. To również nie jest polifill, ponieważ tej metody Array nigdzie nie ma w specyfikacjach ECMAScript.
Joe Maffei,
-2

Owiń swoje sznurki / /i. Jest to prosty sposób na użycie wyrażenia regularnego do zignorowania obudowy

użytkownik3225968
źródło
Pytanie dotyczy sortowania, a nie dopasowania.
user4642212