Làm cách nào để thực hiện phân loại không phân biệt chữ hoa chữ thường trong JavaScript?


220

Tôi có một chuỗi các chuỗi tôi cần sắp xếp trong JavaScript, nhưng theo cách không phân biệt chữ hoa chữ thường. Làm thế nào để thực hiện điều này?

Câu trả lời:


404

Trong (gần như :) một lót

["Foo", "bar"].sort(function (a, b) {
    return a.toLowerCase().localeCompare(b.toLowerCase());
});

Kết quả nào

[ 'bar', 'Foo' ]

Trong khi

["Foo", "bar"].sort();

kết quả trong

[ 'Foo', 'bar' ]

9
Xin lưu ý rằng các tùy chọn nâng cao của localeCompare chưa được hỗ trợ trên tất cả các nền tảng / trình duyệt. Tôi biết chúng không được sử dụng trong ví dụ này, nhưng chỉ muốn thêm cho rõ ràng. Xem MDN để biết thêm thông tin
Ayame__

97
Nếu bạn đang đi để tham gia localeCompare (), bạn chỉ có thể sử dụng khả năng là case-insensitive, ví dụ như:return a.localeCompare(b, 'en', {'sensitivity': 'base'});
Michael Dyck

2
+1 để không gọi toLowerCase()khi localeCompaređã làm điều đó theo mặc định trong một số trường hợp. Bạn có thể đọc thêm về các tham số để truyền tới nó tại đây: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/
Kẻ

3
@Milimetric phù hợp với trang được tham chiếu, tính năng đó không được một số trình duyệt hỗ trợ (ví dụ: IE <11 hoặc Safari). giải pháp được đề cập ở đây là rất tốt, nhưng vẫn sẽ yêu cầu backporting / polyfill cho một số trình duyệt.
3k-

2
Nếu bạn có một mảng lớn, nó có ý nghĩa để sử dụng items.sort(new Intl.Collator('en').compare)cho hiệu suất tốt hơn. (Xem MDN .)
valtlai

60
myArray.sort(
  function(a, b) {
    if (a.toLowerCase() < b.toLowerCase()) return -1;
    if (a.toLowerCase() > b.toLowerCase()) return 1;
    return 0;
  }
);

EDIT: Xin lưu ý rằng ban đầu tôi đã viết điều này để minh họa cho kỹ thuật hơn là có hiệu suất trong tâm trí. Vui lòng tham khảo câu trả lời @Ivan Krechetov để có giải pháp nhỏ gọn hơn.


3
Điều này có thể gọi toLowerCasehai lần trên mỗi chuỗi; sẽ hiệu quả hơn khi lưu trữ các phiên bản thấp hơn của chuỗi trong các biến.
Jacob

Đúng và cảm ơn. Tôi đã viết điều này với sự rõ ràng trong tâm trí, không phải hiệu suất. Tôi đoán tôi nên lưu ý rằng.
ron tornambe

1
@Jacob Để công bằng, câu trả lời được chấp nhận có cùng một vấn đề cơ bản: nó có thể gọi .toLowerCase()nhiều lần cho mỗi mục trong mảng. Ví dụ: 45 cuộc gọi đến chức năng so sánh khi sắp xếp 10 mục theo thứ tự ngược lại. 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
gì cần thiết

47

Đã đến lúc xem lại câu hỏi cũ này.

Bạn không nên sử dụng các giải pháp dựa vào toLowerCase. Chúng không hiệu quả và đơn giản là không hoạt động trong một số ngôn ngữ (ví dụ tiếng Thổ Nhĩ Kỳ). Thích điều này:

['Foo', 'bar'].sort((a, b) => a.localeCompare(b, undefined, {sensitivity: 'base'}))

Kiểm tra tài liệu về tính tương thích của trình duyệt và tất cả những điều cần biết về sensitivitytùy chọn này.


1
Hãy cẩn thận, điều này không được hỗ trợ trong tất cả các công cụ 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;
});

1
hoặcreturn a === b ? 0 : a > b ? 1 : -1;
Devin G Rhode

Điều này có thể sẽ không hoạt động như dự định cho các chuỗi đại diện cho số. Các toán tử số học sẽ sử dụng ngữ nghĩa của các số thay vì các chuỗi. Ví dụ: nếu chúng tôi có ["111", "33"], chúng tôi có thể muốn nó trả về ["111", "33"]vì 1 đến trước 3 trong thứ tự mã ký tự. Tuy nhiên, hàm trong câu trả lời này sẽ trả về ["33", "111"]vì số 33này nhỏ hơn số 111.
Austin Davis

@AustinDavis "33" > "111" === true33 > 111 === false. Nó hoạt động như dự định.
Niet the Dark Tuyệt vời

12

Bạn cũng có thể sử dụng mới Intl.Collator().compare, trên mỗi MDN, nó hiệu quả hơn khi sắp xếp các mảng. Nhược điểm là nó không được hỗ trợ bởi các trình duyệt cũ hơn. MDN tuyên bố rằng nó hoàn toàn không được hỗ trợ trong Safari. Cần xác minh nó, vì nó nói rằng Intl.Collatorđược hỗ trợ.

Khi so sánh số lượng lớn các chuỗi, chẳng hạn như trong việc sắp xếp các mảng lớn, tốt hơn là tạo một đối tượng Intl.Collator và sử dụng hàm được cung cấp bởi thuộc tính so sánh của nó

["Foo", "bar"].sort(Intl.Collator().compare); //["bar", "Foo"]

11

Nếu bạn muốn đảm bảo cùng một thứ tự bất kể thứ tự các phần tử trong mảng đầu vào, đây là cách sắp xếp ổn định :

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

5

Bình thường hóa trường hợp trong .sort()với .toLowerCase().


4

Bạn cũng có thể sử dụng toán tử Elvis:

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

Cung cấp:

biscuit,Bob,charley,fudge,Fudge

Phương thức localeCompare có lẽ vẫn ổn mặc dù ...

Lưu ý: Toán tử Elvis là một dạng "toán tử ternary" dạng ngắn, nếu sau đó, thường là với phép gán.
Nếu bạn nhìn vào ?: Đi ngang, có vẻ như Elvis ...
tức là thay vì:

if (y) {
  x = 1;
} else {
  x = 2;
}

bạn có thể dùng:

x = y?1:2;

tức là khi y đúng, sau đó trả về 1 (đối với gán cho x), nếu không thì trả về 2 (đối với gán cho x).


5
Để được mô phạm, đây không phải là toán tử Elvis. Đây chỉ là một toán tử ternary cơ bản. Một toán tử Elvis thực sự là hợp nhất null, ví dụ, thay vì x = y ? y : z, bạn có thể làm x = y ?: z. Javascript không có toán tử Elvis thực tế, nhưng bạn có thể sử dụng x = y || ztheo cách tương tự.
Charles Wood

3

Các câu trả lời khác cho rằng mảng chứa chuỗi. Phương thức của tôi tốt hơn, bởi vì nó sẽ hoạt động ngay cả khi mảng chứa null, không xác định hoặc các chuỗi không khác.

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

Các nullsẽ được sắp xếp giữa 'nulk' và 'nulm'. Nhưng undefinedsẽ luôn luôn được sắp xếp cuối cùng.


(''+notdefined) === "undefined"vì vậy nó sẽ sắp xếp trước "z"
MattW

Đoán tôi nên tìm định nghĩa của Array.prototype.sort: | bởi vì phần (''+notdefined) === "undefined" thực sự là đúng ... có nghĩa là nếu bạn lật -1 và 1 trong hàm sắp xếp để đảo ngược thứ tự, không xác định vẫn sắp xếp đến cuối. Nó cũng cần được xem xét khi sử dụng hàm so sánh bên ngoài ngữ cảnh của một loại sắp xếp mảng (như tôi đã từng gặp khi đặt câu hỏi này).
MattW

Và bây giờ đã suy nghĩ về Array.prototype.sortđịnh nghĩa đó - vài ý kiến ​​thêm. Đầu tiên, không cần phải có (''+a)- ECMAScript yêu cầu toString()được gọi trên các phần tử trước khi chuyển chúng vào notifyFn. Thứ hai, thực tế là ignoreCasetrả về 1khi so sánh các chuỗi bằng nhau (bao gồm cả các trường hợp bằng nhau) có nghĩa là đặc tả không xác định kết quả nếu có các giá trị trùng lặp (có thể sẽ ổn chỉ với một số giao dịch hoán đổi không cần thiết xảy ra, tôi nghĩ vậy).
MattW

@MattW, đối với tôi, đó undefinedlà một trường hợp đặc biệt, với mọi x x <không xác định và x> không xác định đều sai . Điều đó undefinedluôn luôn là cuối cùng, là sản phẩm phụ của việc thực hiện sắp xếp sắp xếp. Tôi đã cố gắng thay đổi ('' + a) thành đơn giản là a, nhưng không thành công. tôi nhận được TypeError: a.toUpperCase is not a function. Rõ ràng toStringkhông được gọi trước khi gọi so sánhFn.
John Henckel

1
Ah, ok, điều đó làm cho ý nghĩa hoàn hảo. Để undefinedso sánh, không bao giờ được gọi
John Henckel


1

Để hỗ trợ cho câu trả lời được chấp nhận, tôi muốn thêm rằng hàm bên dưới dường như thay đổi các giá trị trong mảng ban đầu được sắp xếp để không chỉ sắp xếp chữ thường mà các giá trị chữ hoa cũng sẽ được thay đổi thành chữ thường. Đây là một vấn đề đối với tôi bởi vì mặc dù tôi muốn thấy Mary bên cạnh mary, tôi không muốn rằng trường hợp của giá trị đầu tiên Mary được thay đổi thành chữ thường.

myArray.sort(
  function(a, b) {
    if (a.toLowerCase() < b.toLowerCase()) return -1;
    if (a.toLowerCase() > b.toLowerCase()) return 1;
    return 0;
  }
);

Trong các thí nghiệm của tôi, hàm sau từ câu trả lời được chấp nhận sắp xếp chính xác nhưng không thay đổi các giá trị.

["Foo", "bar"].sort(function (a, b) {
    return a.toLowerCase().localeCompare(b.toLowerCase());
});

0

Điều này có thể giúp đỡ nếu bạn đã đấu tranh để hiểu:

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/


0
arr.sort(function(a,b) {
    a = a.toLowerCase();
    b = b.toLowerCase();
    if( a == b) return 0;
    if( a > b) return 1;
    return -1;
});

Trong hàm trên, nếu chúng ta chỉ so sánh khi chữ thường hai giá trị a và b, chúng ta sẽ không có kết quả đẹp.

Ví dụ, nếu mảng là [A, a, B, b, c, C, D, d, e, E] và chúng tôi sử dụng hàm trên, chúng tôi có chính xác mảng đó. Nó không thay đổi gì cả.

Để có kết quả là [A, a, B, b, C, c, D, d, E, e], chúng ta nên so sánh lại khi hai giá trị chữ thường bằng nhau:

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

-1

Tôi gói câu trả lời hàng đầu trong một polyfill để tôi có thể gọi .sortIgnoreCase () trên mảng chuỗi

// Array.sortIgnoreCase() polyfill
if (!Array.prototype.sortIgnoreCase) {
    Array.prototype.sortIgnoreCase = function () {
        return this.sort(function (a, b) {
            return a.toLowerCase().localeCompare(b.toLowerCase());
        });
    };
}

Xin đừng bao giờ làm điều này. Chỉ sửa đổi nguyên mẫu của những thứ bạn sở hữu. Đây cũng không phải là một polyfill, vì phương thức Array này không có trong thông số kỹ thuật ECMAScript.
Joe Maffei

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.